Linux 多线程

2018-11-05 08:21:53来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

  

  

1,概念:

  进程:一个正在执行的程序,他是资源分配的最小单位。进程中的事情语言按照一定顺序逐一进行

  线程:又称轻量级线程,程序执行的最小单位,系统独立调度和分派CPU的基本单位,他是进程中一个实体,一个进程中可以有多个线程,这些线程共享进程的所有资                源,线程本身只包含一点必不可少的资源。

  并发:指在同一时刻,只能有执行一条指令,但多个线程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果 。   (看起来同时发生,针对单核

  并行:指在同一时刻,有多条指令在多个处理器上同时执行。 (真正的同时发生

  同步:彼此有依赖关系的调用不应该“同时发生”,而同步就是阻止那些“同时发生”的事情

  异步:与同步相对,任何两个彼此独立的操作时异步的,它表明事情独立发生

 

多线程的优势:

  1)在多处理器中开发程序的并行性

  2)在等待慢速IO操作时,程序可以执行其他操作,提高并发性

  3)模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰

  4)占用较少的系统资源

  多线程不一定要多处理器

 

线程创造 获取ID,生命周期
线程控制 终止、连接、取消、发送信号、清除操作
线程同步 互斥量、读写锁、条件变量
线程高级控制 一次性初始化、线程属性、同步属性、私有数据、安全的fork()

 

2,线程的基本控制

  创建新线程

    线程ID

  线程 进程
标识符类型 pthread_t pid_t
获取ID pthread_seif() getpid()
创建 pthread_create() fork()

      pthread_t : 结构体    unsigned long int   (linux 中提高移植性)        /usr/include/bits/pthreadtypes.h

    

       获取线程ID : pthread_self()

         头文件: #include <pthread.h>

         函数:    pthread_t  pthread_seif();

         返回值 :  线程ID

       编译链接时需要用到线程库  -pthread         ::gcc -pthread  (用到线程都要用)

 

    创建线程:ptnread_create()

        函数:  int  ptnread_create ( ptnread_t *restrict tidp, const ptnread_attr_t *restrict attr , void *(*start_routine)(void *) , void *restrict arg)

        参数:  第一个参数: 新线程的ID,如果成功则新线程的ID回填充到tipe 指向的内存

            第二个参数 : 线程属性 (调整策略,继承性,分离性...)

             第三个参数 : 回调函数  (新线程要执行的函数)

             第四个参数  : 回调函数的参数

        返回值 : 成功   0  ;  失败则返回错误码  

        编译时需要连接库  -pthread   主线程结束后必须有延时让其创建新线程,不然主进程直接被返回。方法用延时或者 下述2

 

 1 #include <stdio.h>
 2 #include <pthread.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <string.h>
 7 void print_id(char *s)
 8 {
 9    pid_t pid;
10    pthread_t tid;
11 
12    pid = getpid();
13    tid = pthread_self();
14 
15   printf("%s pid is %u,the tid is %lu\n",s,pid,tid);
16 
17 }
18 
19 void *thread_fun (void *arg)
20 {
21    print_id(arg);
22    return (void *)0;
23 }
24 
25 int main()
26 {
27    pthread_t ntid;
28    int err;
29 
30    err = pthread_create (&ntid, NULL , thread_fun ,"new thread");
31 
32    if(err!=0)
33    {
34         printf("create new thread failure\n");
35         return 0;
36     }
37 //      printf("create new thread sucess\n");
38         print_id("main thread:");
39         sleep(2);
40         return 0;
41 }
42 ~  
43 
44 结果:
45 main thread: pid is 2833,the tid is 3076491008
46 new thread pid is 2833,the tid is 3076488000
进程创建    

 

  线程的生命周期

  

      1,当C程序运行时,首先运行main()函数;在线程代码中,这个特殊的执行流被称为初始线程或者主线程。可以在初始线程中做任何普通线程可以做的事情

      2,主线程的特殊性在于,它在main() 函数返回的时候,会导致进程的结束,进程内所有的线程也将会结束。为了避免这种情况,需要在主线程中调用  pthread_exit() 函数,这样进程就会等所有线程结束时才终止。

      3,主线程接收参数的方式是通过argc 和argv[] ;而普通线程只有一个参数 void*

      4,绝大多数情况下,主线程在默认堆栈上运行,这个堆栈可以 延长,而普通线程的堆栈会收到限制,一旦溢出就会出错

 

      5,主线程是随着进程的创建而创建,其他线程可以通过调用函数来创建  ,主要为 pthread_create();

      6,新线程可能在当前线程从函数  pthread_cteate ()  返回之前就已经开始运行,甚至可能在返回之前就已经运行完毕

 1 #include "stdio.h"
 2 #include "pthread.h"
 3 #include "stdlib.h"
 4 #include "unistd.h"
 5 #include "sys/types.h"
 6 #include "string.h"
 7 
 8 struct student
 9 {
10    int age;
11    char name[20];
12    char id[5];
13 };
14 
15 void *thread(void *s)
16 {
17 
18    printf("student age is %d,name is %s,id is %s\n",((struct student *)s)->age,((struct student *)s)->name,((struct student *)s)->id);
19    return (void *)0;
20 
21 }
22 
23 
24 int main(int argc,char *argv[])
25 {
26 
27     pthread_t tid;
28     int err;
29 
30 
31      struct student stu;
32       stu.age =20;
33       memcpy(stu.name , "zhangsan" , 20);
34       memcpy(stu.id , "0001" , 5);
35     err = pthread_create(&tid , NULL , thread , (void *)(&stu));
36    if(err != 0)
37       {
38 
39         printf("creat new thread failure\n");
40         return 0;
41 
42         }
43     int i;
44         for(i=0;i<argc;i++)
45         {
46              printf("main thread args is %s\n",argv[i]);
47 
48         }
49         sleep(1);
50         return 0;
51 
52 }
53 ~ 
54 结果:
55 root@ubuntu:/home/xiaozhao# gcc -pthread b.c -o b
56 root@ubuntu:/home/xiaozhao# ./b 21  32 42
57 main thread args is ./b
58 main thread args is 21
59 main thread args is 32
60 main thread args is 42
61 student age is 20,name is zhangsan,id is 0001
验证主进程的特殊

 

 1 #include "stdio.h"
 2 #include "pthread.h"
 3 #include "stdlib.h"
 4 #include "unistd.h"
 5 #include "sys/types.h"
 6 #include "string.h"
 7 
 8 void *thread(void *s)
 9 {
10    int i=0;
11         while(1)
12 {
13    if(i%2 ==1)
14 
15    printf("new thread is %d\n",i);
16     i++;
17    sleep(1);
18 }
19    return (void *)0;
20 
21 }
22 
23 
24 int main(int argc,char *argv[])
25 {
26 
27     pthread_t tid;
28     int err;
29 
30     err = pthread_create(&tid , NULL , thread , "new thread");
31    if(err != 0)
32       {
33 
34         printf("creat new thread failure\n");
35         return 0;
36       }
37     int i=0;
38         while(i<10)
39         {
40         if(i%2 == 0)
41         printf( "main thread is %d\n",i);
42         i++;
43         sleep(1);
44         }
45         return 0;
46 
47 }
48 
49 
50 结果:
51 root@ubuntu:/home/xiaozhao# ./c
52 main thread is 0
53 new thread is 1
54 main thread is 2
55 new thread is 3
56 main thread is 4
57 new thread is 5
58 main thread is 6
59 new thread is 7
60 main thread is 8
61 new thread is 9
62 
63 
64 
65 
66 注意:
67  主线程循环结束,新线程也被结束;而新线程循环结束,不会影响主线程
68     
主线程和新线程交替打印奇偶

 

   

    线程的四个状态

         就绪: 当线程刚被创建处于就绪状态,或者当线程解除阻塞以后也会处于就绪状态。就绪的线程在等待一个可用的处理器,当一个运行的线程被抢占时,它立即又回到就绪状态

         运行 :线程正在运行,在多核系统中,可能同时又多个线程在运行

         阻塞: 线程在等待处理器意外的其他条件(试图加锁一个已经被锁住的互斥量,等待某个条件变量,调用singwait 等待尚未发生的信号,执行无法完成的I\O信号,由于内存错误)

            终止: 线程从启动函数中返回,或者调用 pthread_exit() 函数,或者被取消

 

    回收

           线程的分离属性:

          分离一个正在运行的线程并不影响它,仅仅是通知当前系统该线程结束的时,其所属的资源可以回收,一个没有被分离的线程在终止时会保留其虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸进程”。创建线程时默认时非分离的

          如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未被释放的系统资源和进程资源,包括保存线程返回值的内存空间、堆栈、保存寄存器的内存空间等

          终止被分离的线程会释放所有的系统资源,但是必须释放有该线程占有的程序资源。由malloc() 或 mmap()  分配的内存可以在任何时候由任何线程释放,条件变量、互斥量、信号灯可以由任何线程销毁,只要他们被解锁了或者没有线程等待,但是只有互斥量的主人才能解锁它,所以在线程终止前,需要解锁互斥量

 

*********************************************************************************************************************************

 

  线程终止

    1, 如果进程中的任意一个线程调用了exit() , _Exit ,_exit , 那么整个进程就会终止

    2,不终止进程的退出方式:

      普通的单个线程有以下三种方式退出,不会终止进程:

  •   从启动历程中返回,返回值是线程的退出码    return
  •        线程可以被同一进程中的其他线程取消
  •        线程调用pthread_exit(void *rval) 函数,rval 是退出码
 1 #include "stdio.h"
 2 #include "pthread.h"
 3 #include "stdlib.h"
 4 #include "unistd.h"
 5 #include "sys/types.h"
 6 #include "string.h"
 7 
 8 void *thread_fun (void *s)
 9 {
10   if(strcmp("1",(char *)s)==0)
11              return (void *)1;
12 
13   if(strcmp("2",(char *)s)==0)
14                 pthread_exit((void *)2);
15 
16   if(strcmp("3",(char *)s)==0)
17                exit(3);
18 }
19 
20 
21 int main(int argc, char *argv[])
22 {
23      pthread_t tid;
24      int err;
25 
26      err = pthread_create (&tid , NULL , thread_fun , (void *)argv[1]);
27      if (err !=0)
28     {
29       printf("create new thread failure\n");
30        return 0;
31     }
32       sleep(1);
33       printf("my is main thread\n");
34       return 0;
35 
36 }
37 结果:
38 root@ubuntu:/home/xiaozhao# ./d 1
39 my is main thread
40 root@ubuntu:/home/xiaozhao# ./d 2
41 my is main thread
42 root@ubuntu:/home/xiaozhao# ./d 3
43 root@ubuntu:/home/xiaozhao# 
44 
45 
46 说明:exit()会直接导致进程退出,而前两种不会
三种退出方式

 

 

  线程连接(一个进程等待另一个进程完成在结束)

      int  pthread_join (pthread_t tid ,  void **rval)

  •       调用该函数的线程会一直阻塞,直到指定的线程tid 调用  pthread_exit () ; 从启动历程返回或者被取消
  •                       参数tid 就是指定线程的ID
  •                       参数ravl  是指定线程的返回码,如果线程被取消,那么ravl 被置为  PTHREAD_CANCELED
  •                       该函数调用成功会返回0,失败返回错误码

 

      调用pthread_join 会使指定的线程处于分离状态,如果指定线程已经处于分离状态,那么调用就会失败

      pthread_detach () 可以分离一个线程,线程可以自己分离自己

      int pthread_datach (pthread_t  thread);

        成功返回0 ;  失败返回错误码

 

 1 #include "apb.h"
 2 
 3 void *thread_fun1 (void *s)
 4 {
 5  printf("my is new thread1\n");
 6  return (void *)1;
 7 
 8 }
 9 
10 void *thread_fun2 (void *s)
11 {
12 
13  printf("my is new thread2\n");
14  pthread_detach(pthread_self());
15  pthread_exit((void *)2);
16 }
17 
18 int main()
19 {
20    int err1,err2;
21    pthread_t tid1,tid2;
22    void *rval1,*rval2;
23 
24   
25    err1 = pthread_create (&tid1 , NULL , thread_fun1 , NULL );
26    sleep(1);
27    err2 = pthread_create (&tid2 , NULL , thread_fun2 , NULL );
28    sleep(1);
29    if( err1 || err2 )
30         {
31           printf("create new thread failure\n");
32           return 0;
33         }
34 
35    printf("my is main thread\n");
36    printf("join rval1 is %d\n",pthread_join(tid1,&rval1));
37    printf("join rval2 is %d\n",pthread_join(tid2,&rval2));
38 
39    printf("thread1 exit is %d\n",(int )rval1);
40     printf("thread2 exit is %d\n",(int )rval2);
41 
42 
43 return 0;
44 }
45 ~   
46 
47 结果:
48 my is new thread1
49 my is new thread2
50 my is main thread
51 join rval1 is 0
52 join rval2 is 22
53 thread1 exit is 1
54 thread2 exit is -1216983040
55 
56 问题:
57 如果创建之后不加延时,线程二会比线程一早完成,输出早。。目前不知原因及解决办法
58 my is main thread
59 my is new thread2
60 my is new thread1
61 join rval1 is 0
62 join rval2 is 22
63 thread1 exit is 1
64 thread2 exit is -1216983040
连接,分离

 

 

     线程取消

    取消函数

      int  pthread_cancel (pthread_t tid);

      取消tid指定的线程,成功返回0.但是取消只是发送一个请求,并不意味着等待线程终止,而且发送成功也不意味着tid一定会终止。

 

    取消状态 

      取消状态就是线程对取消信号的处理方式,忽略或者响应。线程创建时默认响应取消信号

      int  pthread_setcancelstate (int state , int *oldstate);

        设置本线程对cancel 信号的反应,state有两种值 :  PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE。。分别表示收到信号后设为CANCLED状态和忽略CANCEL信号 继续运行;old_state 如果不为NULL ,则存入原来的CANCEL 状态以便恢复

 

    取消类型

      取消类型时线程对取消信号的响应方式,立即取消或者延时取消。线程创建时默认为延时取消

        int  pthread_setcanceltype (int type , int *oldtype);

      设置本线程取消动作的执行时机,type由两种取值: PTHREAD_CANCEL_DEFFERED   和   PTHREAD_CANCEL_ASYCHRONOUS  ,  仅当cancel  状态 为ENABLE时有效,分别表示收到信号后继续运行至下一个取消点再退出,和 立即执行取消动作 (退出);  oldtype  如果不为NULL ,则存入运来的取消动作类型值。

 

       取消点

      取消一个线程,他通常需要被取消线程的配合。线程在很多时候会查看自己是否由取消请求。如果有,就主动退出,这些查看是否有取消的地方成为取消点

         很多地方都是包含取消点,包括 pthread_join() , pthread_teacancel() , 等等大多数会阻塞的系统调用

  1 线程被取消
  2 #include "apb.h"
  3 
  4 void *thread_fun (void *s)
  5 {
  6 int stateval;
  7 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL);
  8 if (stateval != 0)
  9 {
 10   printf("set cancel state failure\n");
 11 }
 12 printf("my is new thread\n");
 13 sleep(4);
 14 
 15 printf("about to cancel\n");
 16 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE , NULL);
 17 printf("first cancel point\n");
 18 printf("secong cancel point\n");
 19 return (void *)20;
 20 
 21 
 22 }
 23 
 24 
 25 
 26 int main()
 27 {
 28   pthread_t tid;
 29    int err,cval;
 30    int jval;
 31    void *rval;
 32 
 33    err = pthread_create (&tid , NULL , thread_fun , NULL);
 34    if (err != 0)
 35     {
 36       printf("create new thread failure\n");
 37       return 0;
 38     }
 39     sleep(2);
 40     cval = pthread_cancel(tid);
 41    if(cval !=0)
 42     {
 43       printf("cancel thread failure\n");
 44     }
 45    jval = pthread_join(tid, &rval);
 46 
 47      printf("cancel rval is %d\n",(int )rval);
 48     return 0; 
 49 
 50 
 51 }
 52 
 53 结果:
 54 my is new thread
 55 about to cancel
 56 first cancel point
 57 cancel rval is -1
 58 
 59 
 60 线程忽略取消信号
 61 
 62 #include "apb.h"
 63 
 64 void *thread_fun (void *s)
 65 {
 66 int stateval;
 67 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL);
 68 if (stateval != 0)
 69 {
 70   printf("set cancel state failure\n");
 71 }
 72 printf("my is new thread\n");
 73 sleep(4);
 74 
 75 printf("about to cancel\n");
 76 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL);
 77 printf("first cancel point\n");
 78 printf("secong cancel point\n");
 79 return (void *)20;
 80 
 81 
 82 }
 83 
 84 
 85 
 86 int main()
 87 {
 88   pthread_t tid;
 89    int err,cval;
 90    int jval;
 91    void *rval;
 92 
 93    err = pthread_create (&tid , NULL , thread_fun , NULL);
 94    if (err != 0)
 95     {
 96       printf("create new thread failure\n");
 97       return 0;
 98     }
 99     sleep(2);
100     cval = pthread_cancel(tid);
101    if(cval !=0)
102     {
103       printf("cancel thread failure\n");
104     }
105    jval = pthread_join(tid, &rval);
106 
107      printf("cancel rval is %d\n",(int )rval);
108     return 0; 
109 
110 
111 }
112 结果:
113 
114 my is new thread
115 about to cancel
116 first cancel point
117 secong cancel point
118 cancel rval is 20
119 
120 
121 线程接收到取消信号立即结束:
122 #include "apb.h"
123 
124 void *thread_fun (void *s)
125 {
126 int stateval,type;
127 stateval = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL);
128 if (stateval != 0)
129 {
130   printf("set cancel state failure\n");
131 }
132 printf("my is new thread\n");
133 sleep(4);
134 
135 printf("about to cancel\n");
136 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE , NULL);
137 type = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
138 printf("first cancel point\n");
139 printf("secong cancel point\n");
140 return (void *)20;
141 
142 
143 }
144 
145 
146 
147 int main()
148 {
149   pthread_t tid;
150    int err,cval;
151    int jval;
152    void *rval;
153 
154    err = pthread_create (&tid , NULL , thread_fun , NULL);
155    if (err != 0)
156         {
157           printf("create new thread failure\n");
158           return 0;
159         }
160     sleep(2);
161     cval = pthread_cancel(tid);
162    if(cval !=0)
163         {
164           printf("cancel thread failure\n");
165         }
166    jval = pthread_join(tid, &rval);
167 
168      printf("cancel rval is %d\n",(int )rval);
169     return 0;
170 
171 
172 }
173 结果:
174 
175 my is new thread
176 about to cancel
177 cancel rval is -1
178        
取消函数,状态,类型

 

 

  向线程发送信号

        int  pthread_kill ( pthread_t thread , int sig );

      向指定ID的线程发送SIG信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程。(也就就是说,如果给一个线程发送SIGQUIT,但线程没有实现signal处理函数,则整个进程退出);  

      如果int sig 是0 , 为保留信号,其实并没有发送信号,只是用来判断线程是不是还活着

      成功返回0;失败返回出错码; 

 

   线程的信号:

        进程信号的处理:

          int  sigaction ( int signum ,const struct sigaction *act , struct sigaction *oldact);

          给信号signum设置一个处理函数,函数在sigaction 中指定

                            {

                                                                                                           atc.sa_mask      信号屏蔽字

                              act.as_handler  信号集处理程序

                                  }  

 

         int sigemptyset (sigset_t *set);        清空信号集

         int sigfillset (sigset_t *set);       将所有信号加入信号集

         int sigaddset (sigset_t *set , int signum);    增加一个信号到信号集

         int sigdelset (sigset_t *set , int signum); 删除一个信号到信号集

 

 

       多线程信号屏蔽处理

         int  pthread_sigmask (int how , const sigset_t *set , sigset_t *oidset);

          how  =  SIG_BLOCK  :  向当前信号掩码中添加set ,其中set 表示要阻塞的信号组   

          SIG_UNBLOCK         :  向当前信号掩码中删除set ,其中set 表示取消阻塞的信号组

          SIG_SETMASK    :  将当前信号掩码替换为set ,其中set 表示新的信号掩码。

        在多线程中,新线程的当前信号掩码会继承创造它的那个线程的信号掩码

        一般情况下,被阻塞的信号将不能中断词线程的执行,除非词信号的产生是因为程序出错

        SIGSEGV :  另外不能被忽略处理的信号     SIGKILL 和SIGSTOP 也无法被阻塞

 

             线程清除操作:

        线程可以安排它退出时的清除操作,这与进程的可以用 atexit() 函数安排进程退出时需要调用的函数类似。这样的函数称为线程清理程序。线程可以建立多个清除程序,处理程序记录在栈中,所以这些清理程序执行的顺序与他们注册程序的顺序相反

        pthread_cleanup_push ( void (*trn) (void *) , void *args)  //注册清理程序

        pthread_cleanup_pop  (int  excute)        //   清除清理程序

        这两个必须成对出现,否则编译无法通过

        

        当执行以下操作时,调用清理函数,清理函数的参数由args传入

  •   调用pthread_exit();
  •         响应取消请求
  •         用非零参数来调用pthread_cleanup_pop

        

 1   1 #include "apb.h"
 2   2 
 3   3 void *first_clean(void *s)
 4   4 {
 5   5 printf("%s first clean\n",(char *)s);
 6   6 return (void *)0;
 7   7 }
 8   8 
 9   9 void *second_clean(void *s)
10  10 {
11  11 printf("%s second clean\n",(char *)s);
12  12 return (void *)0;
13  13 }
14  14 
15  15 void *thread_fun1 (void *s)
16  16 {
17  17         printf("new thread1\n");
18  18         pthread_cleanup_push(first_clean,"thread1");
19  19         pthread_cleanup_push(second_clean,"thread1");
20  20 
21  21         pthread_cleanup_pop(1);
22  22         pthread_cleanup_pop(0);
23  23 
24  24         return (void *)1;
25  25 
26  26 }
27  27 
28  28 void *thread_fun2 (void *s)
29  29 {
30  30         printf("new thread2\n");
31  31         pthread_cleanup_push(first_clean,"thread2");
32  32         pthread_cleanup_push(second_clean,"thread2");
33  33 
34  34         pthread_cleanup_pop(1);
35  35         pthread_cleanup_pop(1);
36  36 
37  37         return (void *)1;
38  38 
39  39 }
40  40 
41  41 
42  42 int main()
43  43 {
44  44         pthread_t tid1,tid2;
45  45         int err;
46  46         void *rval1,*rval2;
47  47         err = pthread_create (&tid1 , NULL , thread_fun1 , NULL);
48  48         if(err != 0)
49  49         {
50  50                 printf("create new thread1 failure\n");
51  51                 return;
52  52 
53  53         }
54  54 
55  55         err = pthread_create (&tid2 , NULL , thread_fun2 , NULL);
56  56         if(err != 0)
57  57         {
58  58                 printf("create new thread2 failure\n");
59  59                 return;
60  60 
61  61         }
62            sleep(1);
63  65         return 0;
64  66 
65  67 
66  68 
67  69 }
68 ~        
69 结果:
70 
71 new thread2
72 thread2 second clean
73 thread2 first clean
74 new thread1
75 thread1 second clean
View Code

 

*线程的同步

    互斥量

  当多个线程共享相同的内存时,需要每一个线程看到相同的视图,当一个线程修改变量时,而其他线程也可以读取或者修改这个变量,就需要对这些线程进行同步,确保它们不会访问到无效的变量。

   互斥量的初始化和销毁:

      为了让线程访问数据不产生冲突,这就需要对变量加锁,使得同一时刻只有一个线程可以访问变量。互斥量本身就是锁,访问资源前对互斥量加锁,访问完成后解锁。

      当互斥量加锁后,其他所有需要访问该互斥量的线程都将阻塞

      当互斥量加锁以后,所有因为这个互斥量阻塞的线程都将变为就绪状态,第一个获得cpu 的线程会获得互斥量,变为运行态。而其他线程继续阻塞,在这种访问方式下,互斥量每次只有一个线程能向前执行

      

      互斥量用pthread_mutex_t 类型的数据表示,在使用前需要对互斥量初始化

  • 1,如果是动态分配的互斥量,可以调用 pthread_mutex_init() 函数初始化
  • 2,如果是静态非配的互斥量,还可以把他置为常量PTHREAD_MUTEX_INITIALIZER
  • 3,动态分配的互斥量在释放内存之前需要调用 pthread_mutex_destroy();

 

      int  pthread_mutex_init (pthread_mutex_t  *restrict mutex , const pthread_mutexattr_t  *restrict  attr);

        第一个参数时要初始化的互斥量,第二个参数是互斥量的属性,默认为NULL;

      int  pthread_mutex_destroy  ( pthread_mutex_t  *mutex) ;

        pthread_mutex_t  mutex  =   PTHREAD_MUTEX_INITIALIZER

     加锁 :  

       int  pthread_mutex_lock  ( pthread_mutex_t  *mutex);

        成功返回0 ,失败返回错误码。如果互斥量已经被锁住,则导致该线程阻塞

       int   pthread_mutex_trylock  ( pthread_mutex  *mutex);

           成功返回0 ,失败返回错误码。如果互斥量已经被锁住,不会该线程阻塞    

     

     解锁:

       int   pthread_mutex_unlock  ( pthread_mutex_t  *mutex);

        成功返回0 ;失败返回错误码

 

    

 1 #include "apb.h"
 2 
 3 struct student
 4 {
 5    int id;
 6    int age;
 7    int name;
 8     
 9 }stu;
10 
11 int i;
12 
13 pthread_mutex_t mutex;
14 
15 void *thread_fun1(void *arg)
16 {
17     while(1)
18     {
19         stu.id = i;
20         stu.age = i;
21         stu.name = i;
22         i++;
23         if( stu.id != stu.age || stu.id != stu.name || stu.age != stu.name)
24         {
25         printf("thread1 %d,%d,%d\n",stu.id,stu.age,stu.name);
26         break;
27         }
28     }
29     
30     return (void *)0;
31 }
32 
33 void *thread_fun2(void *arg)
34 {
35     while(1)
36     {
37         stu.id = i;
38         stu.age = i;
39         stu.name = i;
40         i++;
41         if( stu.id != stu.age || stu.id != stu.name || stu.age != stu.name)
42         {
43         printf("thread2%d,%d,%d\n",stu.id,stu.age,stu.name);
44         break;
45         }
46     }
47     
48     return (void *)0;
49 }
50 
51 int main()
52 {
53     pthread_t tid1,tid2;
54     int err;
55 
56     err = pthread_create (&tid1, NULL, thread_fun1, NULL);
57     if(err != 0)
58     {
59        printf("create new thread1 failure\n");
60        return 0;
61     }
62 
63     err = pthread_create (&tid2, NULL, thread_fun2, NULL);
64     if(err != 0)
65     {
66        printf("create new thread2 failure\n");
67        return 0;
68     }
69 
70     pthread_join(tid1, NULL);
71     pthread_join(tid2, NULL);
72     return 0;    
73 
74 }
75 
76 结果:
77 thread2646248,646248,646248
不加锁的问题

 

 1 #include "apb.h"
 2 
 3 struct student
 4 {
 5    int id;
 6    int age;
 7    int name;
 8     
 9 }stu;
10 
11 int i;
12 
13 pthread_mutex_t mutex;
14 
15 void *thread_fun1(void *arg)
16 {
17     while(1)
18     {
19         pthread_mutex_lock(&mutex);        
20         stu.id = i;
21         stu.age = i;
22         stu.name = i;
23         i++;
24         if( stu.id != stu.age || stu.id != stu.name || stu.age != stu.name)
25         {
26         printf("thread1 %d,%d,%d\n",stu.id,stu.age,stu.name);
27         break;
28         }
29         pthread_mutex_unlock(&mutex);
30     }
31     
32     return (void *)0;
33 }
34 
35 void *thread_fun2(void *arg)
36 {
37     pthread_mutex_lock(&mutex);
38     while(1)
39     {
40         stu.id = i;
41         stu.age = i;
42         stu.name = i;
43         i++;
44         if( stu.id != stu.age || stu.id != stu.name || stu.age != stu.name)
45         {
46         printf("thread2%d,%d,%d\n",stu.id,stu.age,stu.name);
47         break;
48         }
49     }
50     
51     pthread_mutex_unlock(&mutex);
52     return (void *)0;
53 }
54 
55 int main()
56 {
57     pthread_t tid1,tid2;
58     int err;
59     
60     err = pthread_mutex_init (&mutex , NULL);
61     if (err != 0)
62         {
63         printf("init mutex failure\n");    
64         return 0;
65         }
66     err = pthread_create (&tid1, NULL, thread_fun1, NULL);
67     if(err != 0)
68     {
69        printf("create new thread1 failure\n");
70        return 0;
71     }
72 
73     err = pthread_create (&tid2, NULL, thread_fun2, NULL);
74     if(err != 0)
75     {
76        printf("create new thread2 failure\n");
77        return 0;
78     }
79 
80     pthread_join(tid1, NULL);
81     pthread_join(tid2, NULL);
82     return 0;    
83 
84 }
加锁之后不会出现错乱

 

  

   读写锁

      读写锁与互斥量类似,不过读写锁由更高的并行性。不过读写锁有更高的并行性。互斥量要么加锁,要么不加锁,而且同一时刻值允许一个线程对其加锁,对于一个变量的读取,完全可以让多个线程同时进行操作。

      定义:   pthread_rwlock_t  rwlock;

      读写锁有三种状态,读模式下加锁,不加锁。一次只有一个线程可以占有写模式的读写锁,但多个线程科同时占有读模式的读写锁。

      读写锁在写加锁状态时,在它被锁之前,所有试图对这个锁加锁的线程都会被阻塞。读写锁在读加锁状态时,所有试图以读模式对其加锁的线程都会获得访问权,但如果线程希望以写模式对其加锁,它必须阻塞直到所有的线程释放。

      当读写锁——读模式加锁时,如果有线程试图以写模式对齐加锁,那么读写锁会阻塞随后的读模式锁请求,这样可以避免读锁长期占用,而写锁达不到请求

      读写锁非常适合对数据结构读次数大于写次数的程序,当它以读模式锁住时,时以共享的方式锁住的;当以写模式锁住时,是以独占模式锁住的

 

      初始化:

        int pthread_rwlock_init(pthread_rwlock_t * rwlock, const pthread_rwlockattr_t * attr);

      读模式加锁:

        int  pthread_rwlock_rdlock ( pthread_rwlock_t  *rwlock);

        int pthread_rwlock_tryrdlock (  pthread_rwlock_t  *rwlock);

      写模式加锁:

        int  pthread_rwlock_wrlock(  pthread_rwlock_t  *rwlock);

        int  pthread_rwlock_trywrlock(  pthread_rwlock_t  *rwlock);

      解锁:

        int  pthread_rwlock_unlock(  pthread_rwlock_t  *rwlock); 

         使用完需要销毁:
            int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

 

      成功返回0;

 

  1 读锁:共享关系
  2 #include "apb.h"
  3 
  4 int num = 0;
  5 pthread_rwlock_t rwlock;
  6 void *thread_fun1(void *s)
  7 {
  8     pthread_rwlock_rdlock(&rwlock);
  9     printf("thread1 printf num %d\n",num);
 10     sleep(5);
 11     printf("thread1 is over\n");
 12 
 13     pthread_rwlock_unlock(&rwlock);
 14     return (void *)1;
 15 }
 16 
 17 void *thread_fun2(void *s)
 18 {
 19     pthread_rwlock_rdlock(&rwlock);
 20     printf("thread2 printf num %d\n",num);
 21     sleep(5);
 22     printf("thread2 is over\n");
 23 
 24     pthread_rwlock_unlock(&rwlock);
 25     return (void *)1;
 26 }
 27 
 28 int main()
 29 {
 30     pthread_t tid1,tid2; 
 31     int err;
 32 
 33     err = pthread_rwlock_init(&rwlock,NULL);
 34      if(err != 0)
 35      {
 36         printf("rwlock init failure\n");
 37         return 0;
 38      }
 39 
 40     err = pthread_create (&tid1, NULL ,thread_fun1 ,NULL);
 41      if(err != 0)
 42      {
 43         printf("create new thread1 failure\n");
 44         return 0;
 45      }
 46 
 47     err = pthread_create (&tid2, NULL ,thread_fun2 ,NULL);
 48      if(err != 0)
 49      {
 50         printf("create new thread2 failure\n");
 51         return 0;
 52      }
 53     
 54     pthread_join(tid1,NULL);
 55     pthread_join(tid2,NULL);
 56 
 57     pthread_rwlock_destroy(&rwlock);
 58     return 0;
 59 
 60 }
 61 
 62 结果:
 63 thread1 printf num 0
 64 thread2 printf num 0
 65 (延时5s)
 66 thread2 is over
 67 thread1 is over
 68 
 69 写锁:
 70 #include "apb.h"
 71 
 72 int num = 0;
 73 pthread_rwlock_t rwlock;
 74 void *thread_fun1(void *s)
 75 {
 76     pthread_rwlock_wrlock(&rwlock);
 77     printf("thread1 printf num %d\n",num);
 78     sleep(5);
 79     printf("thread1 is over\n");
 80 
 81     pthread_rwlock_unlock(&rwlock);
 82     return (void *)1;
 83 }
 84 
 85 void *thread_fun2(void *s)
 86 {
 87     pthread_rwlock_rdlock(&rwlock);
 88     printf("thread2 printf num %d\n",num);
 89     sleep(5);
 90     printf("thread2 is over\n");
 91 
 92     pthread_rwlock_unlock(&rwlock);
 93     return (void *)1;
 94 }
 95 
 96 int main()
 97 {
 98     pthread_t tid1,tid2; 
 99     int err;
100 
101     err = pthread_rwlock_init(&rwlock,NULL);
102      if(err != 0)
103      {
104         printf("rwlock init failure\n");
105         return 0;
106      }
107 
108     err = pthread_create (&tid1, NULL ,thread_fun1 ,NULL);
109      if(err != 0)
110      {
111         printf("create new thread1 failure\n");
112         return 0;
113      }
114 
115     err = pthread_create (&tid2, NULL ,thread_fun2 ,NULL);
116      if(err != 0)
117      {
118         printf("create new thread2 failure\n");
119         return 0;
120      }
121     
122     pthread_join(tid1,NULL);
123     pthread_join(tid2,NULL);
124 
125     pthread_rwlock_destroy(&rwlock);
126     return 0;
127 
128 }
129 
130 结果:
131 thread2 printf num 0
132 (延时5s)
133 thread2 is over
134 thread1 printf num 0
135 (延时5s)
136 thread1 is over
读锁和写锁的不同

 

 

   条件变量

 

        当互斥量被锁住以后,发现当前线程还是无法完成自己的操作,那它应该释放互斥量,让其他线程工作。

    方式:(1,采用轮询的方式,不停的查询需要的条件 ;  2,让系统查询,使用条件变量  pthread_cond_t   cond  ;  )

        

       初始化:

          1,   pthread_cond_t  cond =  PTHREAD_COND_INITALIZER  ;

          2,   int  pthread_cond_init  (  pthread_cond_t  *restrict   cond  ,  const  pthread_condattr_t   *restrict  attr);      (初始量,属性)  默认属性为空  NULL

 

       销毁:

          int  pthread_cond_destroy  (  pthread_cond_t  *cond);

  

       使用:

          条件变量的使用需要配合互斥量

          int pthread_cond_wait  ( pthread_cond_t  *restrict  cond , pthread_mutex_t  *restrict mutex);

          1,使用pthread_cond_wait() 等待条件变为真。传递给pthread_cond_wait () 的互斥量对条件进行保护,调用者把锁住的互斥量传递给函数;

          2,这个函数将线程放到等待条件的线程列表上,然后对互斥量进行解锁,这是个原子操作。当条件满足时这个函数返回,返回后继续对互斥量加锁。

 

          int  pthread_cond_timewait  ( pthread_cond_t  *restrict cond , pthread_mutex_t  *restrict mutex , const struct timespec  *restrict abstime);

          3,  这个函数与pthread_cond_wait 类似 , 只是多一个timeout , 如果到了指定的时间条件还不满足,那么就返回,时间用下面结构体表示

           struct timespec {

              time_t  tv_sec;

              long  tv_nsec;

             };

           注意: 这个时间时绝对时间,例如等待3分钟,就要把当前时间加上3分钟后转换到timespec , 而不是直接将3分钟转化

 

          当条件满足的时候,需要唤醒等待条件的线程

            int  pthread_cond_broadcast  ( pthread_cond_t  *cond);

            int  pthread_cond_signal  ( pthread_cond_t  *cond);

            1,pthread_cond_broadcast   唤醒等待条件的所有线程

            2,pthread_cond_signal   至少唤醒等待条件的某一个线程

            注意: 一定要在条件改变后再唤醒线程

 

                                   

  1 #include "apb.h"
  2 
  3 #define BUFFER_SIZE        5    //产品库存大小
  4 #define PRODUCT_CNT        30   //产品生产总数
  5 
  6 struct produte_cons
  7 {
  8     int buffer[BUFFER_SIZE];  //生产产品值
  9     pthread_mutex_t lock;      //互斥锁  volatile int 
 10     int readpos , writepos;   //读写位置
 11     pthread_cond_t  notempty; //条件变量 ,非空
 12     pthread_cond_t  notfull;  //非满
 13     
 14 }buffer;
 15 
 16 void init(struct produte_cons *p)
 17 {
 18 pthread_mutex_init( &p -> lock,NULL);    //互斥锁
 19 pthread_cond_init( &p -> notempty,NULL);//条件变量
 20 pthread_cond_init( &p -> notfull ,NULL);//条件变量
 21 p -> readpos  =  0;            //读写位置
 22 p -> writepos  =  0;
 23 
 24 }
 25 
 26 void finish(struct produte_cons *p)
 27 {
 28 pthread_mutex_destroy(&p -> lock);
 29 pthread_cond_destroy(&p -> notempty);
 30 pthread_cond_destroy(&p -> notfull);
 31 p -> readpos = 0;
 32 p -> writepos = 0;
 33     
 34 }
 35 
 36 
 37 //储存一个数据到buffer
 38 void put (struct produte_cons *p , int data) //输入产品子函数
 39 {
 40     pthread_mutex_lock (&p ->lock);
 41     if((p -> writepos+1)%BUFFER_SIZE== p->readpos)
 42     {
 43        printf("producer wait fir not full\n");
 44        pthread_cond_wait( &p -> notfull , &p -> lock);
 45 
 46   //这里,生产者 notfull 等待消费者 pthread_cond_signal(&p->notfull);信号
 47           //如果,消费者发送了 signal 信号,表示有了 空闲
 48     }
 49 
 50     p -> buffer[ p -> writepos] = data;
 51     p -> writepos ++;
 52     if (p -> writepos >= BUFFER_SIZE)
 53         p -> writepos = 0;
 54     pthread_cond_signal ( &p -> notempty);
 55     pthread_mutex_unlock (&p -> lock);
 56 }
 57 
 58 //读,移除一个数据从buffer
 59 int get(struct produte_cons *p)
 60 {
 61     int data;
 62 
 63     pthread_mutex_lock (&p -> lock);
 64     
 65     if(p ->readpos == p -> writepos)
 66     {
 67         printf("consumer wait for not empty\n");
 68         pthread_cond_wait (&p ->notempty , &p ->lock);
 69     }
 70 
 71     data = p ->buffer[p ->readpos];
 72     p ->readpos ++;
 73 
 74     if(p ->readpos >= BUFFER_SIZE)
 75         p ->readpos = 0;
 76     pthread_cond_signal (&p -> notfull);
 77     pthread_mutex_unlock (&p -> lock);
 78     
 79     return data;
 80 }
 81 
 82 void *producer(void *data) //子线程  ,生产
 83 {
 84    int n;
 85    for(n = 1;n<50; ++n)//生产50个产品
 86    {
 87        sleep(1);
 88        printf("put the %d produte ...\n",n);
 89        put(&buffer ,n);
 90        printf("put the %d produte succes\n",n);
 91        
 92    }
 93 
 94    printf("producer stopped\n");
 95    return NULL;
 96 }
 97 
 98 
 99 void *consumer (void *data)
100 {
101    static int cnt = 0;
102    int num;
103    while(1)
104     {
105     sleep(2);
106     printf("get product ...\n");
107     num = get(&buffer);
108     printf("get the %d product success\n",num);
109     if (++cnt == PRODUCT_CNT)
110         break;
111         
112     }
113    printf("consumer stopped\n");    
114    return NULL;
115     
116 }
117 
118 int main(int argc, char *argv[])
119 {
120     
121     pthread_t th_a,th_b;
122     void *retval;
123 
124     init(&buffer);
125 
126     pthread_create(&th_a , NULL , producer ,0);
127     pthread_create(&th_b , NULL , consumer ,0);
128     
129     pthread_join(th_a , &retval);
130     pthread_join(th_b , &retval);
131 
132     finish(&buffer);
133 
134     return 0;
135 }
136 
137 
138 结果:
139 
140 put the 1 produte ...
141 put the 1 produte succes
142 get product ...
143 get the 1 product success
144 put the 2 produte ...
145 put the 2 produte succes
146 put the 3 produte ...
147 put the 3 produte succes
148 get product ...
149 get the 2 product success
150 put the 4 produte ...
151 put the 4 produte succes
152 put the 5 produte ...
153 put the 5 produte succes
154 get product ...
155 get the 3 product success
156 put the 6 produte ...
157 put the 6 produte succes
158 put the 7 produte ...
159 put the 7 produte succes
160 get product ...
161 get the 4 product success
162 put the 8 produte ...
163 put the 8 produte succes
164 put the 9 produte ...
165 producer wait fir not full
166 get product ...
167 get the 5 product success
168 put the 9 produte succes
169 put the 10 produte ...
170 producer wait fir not full
171 get product ...
172 get the 6 product success
173 put the 10 produte succes
单生产者,单消费者

 

 

 

线程的高级属性:

  1)一次性初始化:

       通常当初始化应用程序时,可以比较容易地将其放在main()中。但当写一个库函数时,就不能在main() 函数中初始化了,可以用静态初始化,但使用一次性初始化会更简单

      首先定义一个pthread_once_t 的变量,这个变量要用宏PTHREAD_ONCE_INIT 初始化。然后创建一个与控制变量相关的初始化函数

      pthread_once_t once_control = PTHREAD_ONCE_INIT

      void init_routine()

      {

        //初始化互斥量

        //初始化读写锁

      }

 

      接下来就可以在任何时候调用pthread_once函数

      int pthread_once  ( pthread_once_t * once_control , void ( *init_routine)(void));      eg: pthread_once(&once , thread_fun1);

      功能:此函数使用初值为PTHREAD_ONCE_INIT 的once_control  变量  保证init_routine() 函数在本进程执行序列中仅执行一次。在多线程编程环境下,尽管 pthread_once()  调用会出现在多个线程中,  init_routine()  函数仅执行一次,在哪个线程中执行是由内核调度决定的。

 

      实际“一次性函数” 的执行状态由三种 :NEVER 从未(0)   ;  IN_PROGRESS  (1)正在执行   ;    DONE  完毕  (2)    .用once_control来表示pthread_once() 的执行状态:

        1) 如果once_control的初值为0 ,那么pthread_once 从未执行过,init_routine()函数会执行

     2)  如果once_control的初值为1,则由于所有pthread_once() 都必须等待其中一个激发“已执行一次”的信号,因此所有pthread_once()  都会陷入等待,init_control()就无法执行

     3)  如果once_control的初值为2,则表示pthread_once()  已执行过一次,从而所有pthread_once()  都会立即返回,init_control()就没有机会执行

 

      当pthread_once 的函数成功返回,once_control就会被设置为2.

 

 

****************************************************************************************************************************************************************************************************************************************************************

 

线程属性:

  1,线程的属性

    线程的属性用pthread_attr_t 类型结构表示,在创建线程的时候可以不用传入NULL,而是传入一个pthread_attr_t 结构,由用户自己配置线程的属性

    pthread_attr_t 类型对应用线程是不透明的,也就是说应用不需要了解有关属性对象内部结构的任何细节,因而可以增加线程的可移植性

 

    线程的属性:

名称 描述
detachstate 线程的分离状态
guardsize 线程栈末尾的警戒区域大小(字节数)
stacksize 线程栈的最低地址
stacksize 线程栈的大小(字节数)

      并不是所有的系统都支持线程的这些属性,因此需要检查当前系统是否支持;还有一些属性不包括在pthread_attr_t 内,如:线程的可取消状态,取消类型,并发度。

  

    线程属性初始化和销毁

      线程属性初始化:

            int  pthread_attr_init  ( pthread_attr_t  *attr);

      线程属性销毁:

            int  pthread_attr_destroy(  pthread_attr_t  *attr);

      如果在点用pthread_attr_init 初始化属性的时候分配了内存空间,那么pthread_attr_destroy() 将释放内存空间。除此之外,pthread_attr_destroy 还会用无效的值初始化pthread_dttr_t 对象,因此如果该属性对象呗误用,会导致创建线程失败。

            

  2,线程的分离属性:

       分离属性的使用方法:

      如果在创建线程的时候就知道不需要了解线程的终止状态,那么可以修改pthread_attr_t  结构体的detachstate 属性,让线程以分离状态启动。可以使用pthread_attr_setdetachstate函数来设置线程的分离状态属性。线程的分离属性由两种合法值:

          PTHREAD_CREATE_DETACHED    分离的

        PTHREAD_CREATE_JOINABLE      非分离的 ,可以连接

      int  pthread_attr_setdetachstate ( pthread_attr_t  *attr , int detachstate);

      int  pthread_attr_getdetachstate ( pthread_attr_t  *attr , int  *detachstate);          可获取线程的分离状态属性

 

      设置线程分离的步奏:

      1,定义线程属性变量  pthread_attr_t  attr

      2,初始化 attr,, pthread_attr_init  (&attr);

      3,设置线程为分离或非分离   pthread_attr_setdetachstate ( &attr , detachstate);

      4,创建线程pthread_create  ( &tid , &attr , thread_fun , NULL);

     所有的系统都会支持线程的分离状态属性。

 

 1 #include "apb.h"
 2 
 3 int num = 0;
 4 pthread_attr_t attr;
 5 void *thread_fun1(void *s)
 6 {
 7     printf("thread1 printf num %d\n",num);
 8     return (void *)1;
 9 }
10 
11 void *thread_fun2(void *s)
12 {
13     printf("thread2 printf num %d\n",num);
14     return (void *)2;
15 }
16 
17 int main()
18 {
19     pthread_t tid1,tid2;
20     int err;
21     
22     pthread_attr_init(&attr);
23     pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_JOINABLE);
24 //    pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
25 
26     err = pthread_create (&tid1, &attr ,thread_fun1 ,NULL);
27      if(err != 0)
28      {
29         printf("create new thread1 failure\n");
30         return 0;
31      }
32 
33     err = pthread_create (&tid2, &attr ,thread_fun2 ,NULL);
34      if(err != 0)
35      {
36         printf("create new thread2 failure\n");
37         return 0;
38      }
39     
40     pthread_join(tid1,NULL);
41     pthread_join(tid2,NULL);
42 
43     pthread_attr_destroy(&attr);
44 
45          return 0;
46 }
47 
48 结果:
49 thread2 printf num 0
50 thread1 printf num 0
51 
52 
53 如果用DETACHED   ,则join失败,新进程不会执行
分离的两种值

 

 

  3,线程栈属性:用ulimit -s 查看默认栈大小

     1、 线程的栈大小与地址;

        对于进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈。因此它的大小通长不是问题。但对线程来说,同样的虚拟地址被所有的线程共享。如果应用程序使用了太多的线程,致使栈累计超过可用的虚拟地址空间,这个时候就需要减少线程默认栈的大小。另外,如果线程分配了大量的自动变量或者线程的栈帧太深,那么这个时候需要的栈要比默认的大。

 

        如果用完了虚拟地址空间,可以使用malloc  或者  mmap  来为其他栈分配空间,并修改栈的位置。

      修改栈属性:

        int  pthread_attr_setstack  (  pthread_attr_t  *attr , void  *stackaddr , size_t  stacksize);

      获取栈属性

        int  pthread_attr_getstack  (  pthread_attr_t  *attr , void  **stackaddr , size_t  *stacksize);

 

      参数:stackaddr  是栈的内存单元最低地址 , 参数  stacksize 是栈的大小。需要注意:  stackaddr 并不一定是栈的开始,对于一些处理器,栈的地址是从高往低,那么这时 stackaddr 时栈的结尾。

 

      当然也可以单独获取或者修改栈的大小,而不去修改栈的地址。对于栈大小设置,不能小于PTHREAD_STACK_MIN (需要头文件  limit.h)

      设置栈大小:

        int  pthread_attr_setstacksize ( pthread_attr_t  *attr , size_t  stacksize);

      获取栈大小:

          int  pthread_attr_getstacksize (  pthread_attr_t  *attr , aisze_t  *stacksize);

       对于栈大小的设置,在创建线程之后,还可以修改。

 

      对于遵循POSIX 标准的系统来说,不一定要支持线程的栈属性,需要检查

        1)在编译阶段使用

       _POSIX_THREAD_ATTR_STACKADDR   和   _POSIX_THREAD_ATTR_STACKSIZE  符号来检查系统是否支持线程栈属性

        2)在运行阶段

       _SC_THREAD_ATTR_STACKADD  和  _SC_THREAD_THREAD_ATTR_STACKSIZE  传给syconf () 函数检查系统对线程栈属性的支持

 

    2、 栈尾警戒区

      线程属性guardsize 控制着线程栈末尾以后用以避免栈溢出的扩展内存的大小,这个属性默认PAGESIZE 个字节 。可以把它设置为0 , 这样就不会提供警戒缓冲区。同样的,如果修改了stackaddr , 系统会认为用户管理栈,警戒缓存区会无效。

 

      设置guardsize:

        int  pthread_attr_setgusrdsize (  pthread_attr_t  *attr , size_t  guardsize);

      获取guardsize:

        int  pthread_attr_getguardsize (  pthread_attr_t  *attr  ,size_t  *guardsize);

 

 1 #include "apb.h"
 2 
 3 int num = 0;
 4 pthread_attr_t attr;
 5 void *thread_fun1(void *s)
 6 {
 7     size_t stacksize;
 8 #ifdef _POSIX_THREAD_ATTR_STACKSIZE
 9     pthread_attr_getstacksize(&attr , &stacksize);
10     printf("thread1 init stack size is  %d\n",stacksize);
11     pthread_attr_setstacksize(&attr , 16400);
12         pthread_attr_getstacksize(&attr , &stacksize);
13     printf("thread1 after stack size is  %d\n",stacksize);
14 
15 #endif
16     return (void *)1;
17 }
18 
19 
20 int main()
21 {
22     pthread_t tid1;
23     int err;
24     
25     pthread_attr_init(&attr);
26     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
27 
28 #ifdef _POSIX_THREAD_ATTR_STACKSIZE
29     pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
30 #endif
31     err = pthread_create (&tid1, NULL ,thread_fun1 ,NULL);
32      if(err != 0)
33      {
34         printf("create new thread1 failure\n");
35         return 0;
36      }
37 
38     
39     pthread_join(tid1,NULL);
40     return 0;
41 }
42 
43 结果:
44 thread1 init stack size is  16384
45 thread1 after stack size is  16400
栈大小查看修改

 

 

  4,线程的同步属性:

    1)互斥量的属性:

       互斥量的属性用 pthread_mutexattr_t 类型数据表示,使用之前必须初始化,结束必须销毁:

      初始化:

        int  pthread_mutexattr_init (pthread_mutexattr_t  *attr);

      销毁:

        int  pthread_mutexattr_destroy ( pthread_mutexattr_t  *attr);

      

        1,进程共享属性

          共享进程属性由两种值:

        PTHREAD_PROCESS_PRIVATE      这个是默认值,同一个进程中的多个线程间同一个同步对象

        PTHREAD_PROCESS_SHARED      这个属性可以使互斥量在多个进程中同步进行,如果互斥量在多个进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程

 

        设置互斥量进程共享属性

        int  pthread_mutexattr_getpshared  (  const  pthread_mutexattr_t  *restrict attr , int  *restrict pshared);

        int  pthread_mutexattr_setpshared  (  pthread_mutexattr_t  *attr , int pshared);

  

        进程共享属性需要检测系统是否支持,可以检测宏  _POSIX_THREAD_PROCESS_SHARED

        

        2,类型属性

互斥量类型 没有解锁时再次加锁 不占用时加锁 已解锁时加锁

PTHREAD_MUTEX_NORMAL  (默认类型)

死锁 未定义 未定义
PTHREAD_MUTEX_ERRORCHEK    (错误检查) 返回错误 返回错误 返回错误
PTHREAD_MUTEX_RECURSIVE    (递归) 允许 返回错误 返回错误
PTHREAD_MUTEX_DEFAULT  (默认,请求系统设置为其他三种) 未定义 未定义 未定义

 

            设置互斥量的类型属性:

              int  pthread_mutexattr_gettype (  const pthread_mutexattr_t  *attr , int  *restrict  type);

              int  pthread_mutexattr_settype (  pthread_mutexattr_t  *attr , int  type);

 

 1 #include "apb.h"
 2 
 3 int main()
 4 {
 5     char *shm = "myshm";
 6     char *shm1 = "myshm1";
 7     int shm_id,shm_id1;
 8     char *buf;
 9     pid_t pid;
10 
11     //打开共享内存
12     shm_id = shm_open(shm , O_RDWR|O_CREAT , 0644);
13     //调整共享内存大小
14     ftruncate(shm_id ,100);
15     //映射共享内存,MAP_SHARED 属性表明,对共享内存的任何修改都会影响其他进程
16     buf = (char *)mmap(NULL , 100 , PROT_READ|PROT_WRITE , MAP_SHARED , shm_id ,0);
17 
18     pid = fork();
19     if(pid == 0)
20     {
21         //休眠一秒让父进程先运行
22         sleep(1);    
23         printf("i am child proccess\n");
24 
25         //将共享内存内容改为hello
26         memcpy (buf , "hello" , 6);
27         printf("child buf is : %s\n",buf);
28         
29     }
30     else if (pid >0)
31     {
32         printf("i an parent proccess\n");    
33         
34         //修改内容为world
35         memcpy(buf , "world",6);
36         sleep(3);
37         printf("parent buf is : %s\n",buf);
38     }
39     
40     //解除映射
41     munmap(buf , 100);
42     //消除共享内存
43     shm_unlink(shm);
44     return 0;
45 }
46 
47 gcc a.c -lrt -o a
48 
49 
50 结果:
51 i an parent proccess
52 i am child proccess
53 child buf is : hello
54 parent buf is : hello
View Code

 

 1 #include "apb.h"
 2 int main()
 3 {
 4     char *shm = "myshm";
 5     char *shm1 = "myshm1";
 6     int shm_id,shm_id1;
 7     char *buf;
 8     pid_t pid;
 9     
10     pthread_mutex_t *mutex;
11     pthread_mutexattr_t mutexattr;
12 
13     //打开共享内存
14     shm_id1 = shm_open(shm1 , O_RDWR|O_CREAT , 0644);
15     //调整共享内存大小
16     ftruncate(shm_id1 ,100);
17     //映射共享内存,MAP_SHARED 属性表明,对共享内存的任何修改都会影响其他进程
18     mutex = (pthread_mutex_t *)mmap(NULL , 100 , PROT_READ|PROT_WRITE , MAP_SHARED , shm_id1 ,0);
19     
20     pthread_mutexattr_init(&mutexattr);
21 #ifdef _POSIX_THREAD_PROCESS_SHARED
22     pthread_mutexattr_setpshared(&mutexattr , PTHREAD_PROCESS_SHARED);
23 #endif
24     pthread_mutex_init(mutex, &mutexattr);
25     //打开共享内存
26     shm_id = shm_open(shm , O_RDWR|O_CREAT , 0644);
27     //调整共享内存大小
28     ftruncate(shm_id ,100);
29     //映射共享内存,MAP_SHARED 属性表明,对共享内存的任何修改都会影响其他进程
30     buf = (char *)mmap(NULL , 100 , PROT_READ|PROT_WRITE , MAP_SHARED , shm_id ,0);
31 
32     pid = fork();
33     if(pid == 0)
34     {
35         //休眠一秒让父进程先运行
36         sleep(1);    
37         printf("i am child proccess\n");
38 
39         pthread_mutex_lock(mutex);
40         //将共享内存内容改为hello
41         memcpy (buf , "hello" , 6);
42         printf("child buf is : %s\n",buf);
43         pthread_mutex_unlock(mutex);
44     }
45     else if (pid >0)
46     {
47         printf("i an parent proccess\n");    
48         
49         pthread_mutex_lock(mutex);
50         //修改内容为world
51         memcpy(buf , "world",6);
52         sleep(3);
53         printf("parent buf is : %s\n",buf);
54         pthread_mutex_unlock(mutex);
55     }
56     
57 
58     pthread_mutexattr_destroy(&mutexattr);
59     pthread_mutex_destroy(mutex);
60 
61     munmap(buf,100);
62     shm_unlink(shm1);
63     //解除映射
64     munmap(buf , 100);
65     //消除共享内存
66     shm_unlink(shm);
67     return 0;
68 }
69 
70 
71 gcc a.c -lrt -pthread -o a
72 
73 
74 
75 结果:
76 i an parent proccess
77 i am child proccess
78 parent buf is : world
79 child buf is : hello
View Code

 

      2)读写锁

          只有一个进程共享属性

          初始化:

           int  pthread_rwlockattr_init(  pthread_rwlockattr_t  *attr);

          销毁:

           int  pthread_rwlockattr_destroy (  pthread_rwlockattr_t  *attr);

          设置读写锁的进程共享属性:

           int  pthread_rwlockattr_getpshared (  const pthread_rwlockattr_t  *restrict attr , int  *restrict pshared);

           int  pthread_rwlockattr_setpshared (  pthread_rwlock_t  *attr , int  pshared);

 

      3)条件变量

          也有进程共享属性

          初始化:

           int  pthread_condattr_init(  pthread_condattr_t  *attr);

          销毁:

           int  pthread_condattr_destroy (  pthread_condattr_t  *attr);

          设置读写锁的进程共享属性:

           int  pthread_condattr_getpshared (  const  pthread_condattr_t  *restrict attr , int  *restrict pshared);

           int  pthread_condattr_setpshared (  pthread_rwlock_t  *attr , int  pshared);

 

    5,线程的私有数据

        应用程序设计中有必要提供一种变里,使得多个函数多个线程都可以访问这个变量( 看起来是个全局变里),但是线程对这个变里的访问都不会彼此产生影响(貌似不是全局变里哦〉,但是你需要这样的数据,比如errno。那么这种数据就是线程的私有数据,尽管名字相同,但是每个线程访问的都是数据的副本。

        在使用私有数据之前,首先要创建一个与私有数据相关的键,来获取对私有数据的访问权限。这个间哦的类型是  pthread_key_t

 

        int  pthread_key_create ( pthread_key_t  *key , void  (*destructor)(void *)  );

 

        创建的键放在key指向的内存单元里,destructor 是与键相关的析构函数 。当线程调用 pthread_exit  或者  使用return  返回,析构函数就会被调用。当析构函数调用的时候,它只有一个参数,这个参数是与key关联的那个数据的地址,因此可以在析构函数中将这个数据销毁。  键使用完也可以销毁,与它关联的数据并没有销毁

 

        int  pthread_key_delete (  pthread_key_t  key);

 

        有了这个键之后,就可以将私有数据和键关联起来,这样就可以通过键来找到数据。所有的线程都可以访问这个键,但他们可以为键关联不同的数据(名字一样,值却不同的全局变量)

 

        int  pthread_setspecific  (  pthread_key_t  key , const  void  *value);        将私有数据与键关联

 

        void  *   pthread_getspecific (  pthread_key_t  key);               获取私有数据的地址,如果没有数据与key 关联,那么返回空

 

 1 #include "apb.h"
 2 
 3 pthread_key_t  key;
 4 
 5 void *thread_fun1(void *s)
 6 {
 7     printf("thread1 start\n");
 8     int a =1;
 9     pthread_setspecific(key , (void *)a);
10     sleep(2);
11     printf("thread1 key -> data is %d\n", (int)pthread_getspecific(key));
12     return (void *)1;
13 }
14 
15 void *thread_fun2(void *s)
16 {
17     sleep(1);
18     printf("thread2 start\n");
19     int a =2;
20     pthread_setspecific(key , (void *)a);
21     
22     printf("thread2 key -> data is %d\n",(int) pthread_getspecific(key));
23 }
24 
25 int main()
26 {
27     pthread_t tid1,tid2;
28     int err;
29     
30     pthread_key_create(&key,NULL);
31     err = pthread_create (&tid1, NULL ,thread_fun1 ,NULL);
32      if(err != 0)
33      {
34         printf("create new thread1 failure\n");
35         return 0;
36      }
37 
38     err = pthread_create (&tid2, NULL ,thread_fun2 ,NULL);
39      if(err != 0)
40      {
41         printf("create new thread2 failure\n");
42         return 0;
43      }
44     
45     pthread_join(tid1,NULL);
46     pthread_join(tid2,NULL);
47 
48     pthread_key_delete(key);
49 }
50 结果:
51 thread1 start
52 thread2 start
53 thread2 key -> data is 2
54 thread1 key -> data is 1
私有化

 

 

    6,线程与fork()

        

       当线程调用fork函数时,就为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥量、读写锁、条件变量的状态继承过来。也就是说,如果父进程中互斥量是锁着的,那么在子进程中互斥量也是锁着的(尽管子进程自己还没有来得及lock),这是丰常不安全的,因为不是子进程自己锁住的,它无法解锁。

 

          子进程内部只有一个线程,由父进程中调用fork函数的线程副本构成。如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_ mutex_ lock副本,这样子进程就有机会去解锁了。或者互斥量根本就没被加锁,这样也是可以的,但是你不能确保永远是这样的情况。

          pthread_ atfork函数给你创造了这样的条件,它会注册三个函数

 

          int  pthread_ atfork  (void (*prepare) (void)  ,   void (*parent) (void)  ,   void (*child) (void)  );

 

          prepare是在fork调用之前会被调用的,parent在fork返回父进程之前调用,child在fork返回子进程之前调用。如果在prepare中加锁所有的互斥量,在parent和child中解锁所有的互斥量,那么在fork返回之后,互斥量的状态就是未加锁。

          

      可以有多个pthread_ atfork 函数,这是也就会有多个处理程序( prepare, parent, child) 。多个prepare的执行顺序与注册顺序相反,而parent和child的执行顺序与注册顺序相同。

 

  

 1 #include "apb.h"
 2 pthread_mutex_t mutex  =  PTHREAD_MUTEX_INITIALIZER;
 3 
 4 void *thread_fun(void *s)
 5 {
 6    sleep(1);
 7    pid_t pid;
 8 
 9    pid = fork();
10    if(pid==0)
11    {
12     pthread_mutex_lock(&mutex);
13     printf("child process\n");
14     pthread_mutex_unlock(&mutex);
15        
16    }
17    if(pid>0)
18    {
19     pthread_mutex_lock(&mutex);
20     printf("parent process\n");
21     pthread_mutex_unlock(&mutex);
22    }
23 }
24 
25 int main()
26 {
27    pthread_t tid;
28 
29    if(pthread_create(&tid , NULL , thread_fun,NULL))
30    {
31     printf("create thread failure\n");
32     return;
33        
34    }
35    pthread_mutex_lock(&mutex);
36    sleep(2);
37    printf("main\n");
38    pthread_mutex_unlock(&mutex);
39    pthread_join(tid ,NULL);
40 
41    pthread_mutex_destroy(&mutex);
42    return 0;
43     
44     
45 }
46 
47 
48 结果:
49 main
50 parent process
51 
52   PID TTY          TIME CMD
53  2375 pts/0    00:00:00 su
54  2376 pts/0    00:00:00 bash
55  2720 pts/0    00:00:00 vi
56  3036 pts/0    00:00:00 c
57  3047 pts/0    00:00:00 ps
58 
59 c 进程阻塞
子进程继承加锁,阻塞
 1 #include "apb.h"
 2 pthread_mutex_t mutex  =  PTHREAD_MUTEX_INITIALIZER;
 3 
 4 void *thread_fun(void *s)
 5 {
 6    sleep(1);
 7    pid_t pid;
 8 
 9    pthread_mutex_lock(&mutex);
10    pid = fork();
11    if(pid==0)
12    {
13 //    pthread_mutex_lock(&mutex);
14 //    printf("child process\n");
15     pthread_mutex_unlock(&mutex);
16     printf("child process\n");
17        
18    }
19    if(pid>0)
20    {
21 //    pthread_mutex_lock(&mutex);
22 //    printf("parent process\n");
23     pthread_mutex_unlock(&mutex);
24     printf("parent process\n");
25    }
26 }
27 
28 int main()
29 {
30    pthread_t tid;
31 
32    if(pthread_create(&tid , NULL , thread_fun,NULL))
33    {
34     printf("create thread failure\n");
35     return;
36        
37    }
38   // pthread_mutex_lock(&mutex);
39   // sleep(2);
40    printf("main\n");
41   // pthread_mutex_unlock(&mutex);
42    pthread_join(tid ,NULL);
43 
44 //   pthread_mutex_destroy(&mutex);
45    return 0;
46     
47     
48 }
49 
50 结果:
51 main
52 parent process
53 child process
在新线程中加锁fock之前,父子进程拷贝加锁,与其加锁可以配对
 1 #include "apb.h"
 2 pthread_mutex_t mutex  =  PTHREAD_MUTEX_INITIALIZER;
 3 
 4 void prepare()
 5 {
 6      pthread_mutex_lock(&mutex);
 7     printf("i am prepare\n");
 8 }
 9 
10 void parent()
11 {
12    pthread_mutex_unlock(&mutex);
13    printf("i am parent\n");
14     
15 }
16 
17 void child()
18 {
19     pthread_mutex_unlock(&mutex);
20     printf("i am child\n");
21     
22 }
23 void *thread_fun(void *s)
24 {
25    sleep(1);
26    pid_t pid;
27    pthread_atfork(prepare , parent ,child);
28    pid = fork();
29    if(pid==0)
30    {
31     pthread_mutex_lock(&mutex);
32     printf("child process\n");
33     pthread_mutex_unlock(&mutex);
34        
35    }
36    if(pid>0)
37    {
38     pthread_mutex_lock(&mutex);
39     printf("parent process\n");
40     pthread_mutex_unlock(&mutex);
41    }
42 }
43 
44 int main()
45 {
46    pthread_t tid;
47 
48    if(pthread_create(&tid , NULL , thread_fun,NULL))
49    {
50     printf("create thread failure\n");
51     return;
52        
53    }
54   // pthread_mutex_lock(&mutex);
55   // sleep(2);
56    printf("main\n");
57   // pthread_mutex_unlock(&mutex);
58    pthread_join(tid ,NULL);
59 
60 //   pthread_mutex_destroy(&mutex);
61    return 0;
62     
63     
64 }
65 
66 
67 结果:
68 
69 main
70 i am prepare
71 i am parent
72 parent process
73 i am child
74 child process
采用pthread_atfork函数

 

 

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:分布式系统监视zabbix讲解四之可视化--技术流ken

下一篇:Linux基础命令---swapon