• Linux 多线程


      

      

    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
    ",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
    ");
    35         return 0;
    36     }
    37 //      printf("create new thread sucess
    ");
    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
    ",((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
    ");
    40         return 0;
    41 
    42         }
    43     int i;
    44         for(i=0;i<argc;i++)
    45         {
    46              printf("main thread args is %s
    ",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
    ",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
    ");
    35         return 0;
    36       }
    37     int i=0;
    38         while(i<10)
    39         {
    40         if(i%2 == 0)
    41         printf( "main thread is %d
    ",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 等待尚未发生的信号,执行无法完成的IO信号,由于内存错误)

                终止: 线程从启动函数中返回,或者调用 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
    ");
    30        return 0;
    31     }
    32       sleep(1);
    33       printf("my is main thread
    ");
    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
    ");
     6  return (void *)1;
     7 
     8 }
     9 
    10 void *thread_fun2 (void *s)
    11 {
    12 
    13  printf("my is new thread2
    ");
    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
    ");
    32           return 0;
    33         }
    34 
    35    printf("my is main thread
    ");
    36    printf("join rval1 is %d
    ",pthread_join(tid1,&rval1));
    37    printf("join rval2 is %d
    ",pthread_join(tid2,&rval2));
    38 
    39    printf("thread1 exit is %d
    ",(int )rval1);
    40     printf("thread2 exit is %d
    ",(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
    ");
     11 }
     12 printf("my is new thread
    ");
     13 sleep(4);
     14 
     15 printf("about to cancel
    ");
     16 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE , NULL);
     17 printf("first cancel point
    ");
     18 printf("secong cancel point
    ");
     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
    ");
     37       return 0;
     38     }
     39     sleep(2);
     40     cval = pthread_cancel(tid);
     41    if(cval !=0)
     42     {
     43       printf("cancel thread failure
    ");
     44     }
     45    jval = pthread_join(tid, &rval);
     46 
     47      printf("cancel rval is %d
    ",(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
    ");
     71 }
     72 printf("my is new thread
    ");
     73 sleep(4);
     74 
     75 printf("about to cancel
    ");
     76 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE , NULL);
     77 printf("first cancel point
    ");
     78 printf("secong cancel point
    ");
     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
    ");
     97       return 0;
     98     }
     99     sleep(2);
    100     cval = pthread_cancel(tid);
    101    if(cval !=0)
    102     {
    103       printf("cancel thread failure
    ");
    104     }
    105    jval = pthread_join(tid, &rval);
    106 
    107      printf("cancel rval is %d
    ",(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
    ");
    131 }
    132 printf("my is new thread
    ");
    133 sleep(4);
    134 
    135 printf("about to cancel
    ");
    136 stateval  = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE , NULL);
    137 type = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    138 printf("first cancel point
    ");
    139 printf("secong cancel point
    ");
    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
    ");
    158           return 0;
    159         }
    160     sleep(2);
    161     cval = pthread_cancel(tid);
    162    if(cval !=0)
    163         {
    164           printf("cancel thread failure
    ");
    165         }
    166    jval = pthread_join(tid, &rval);
    167 
    168      printf("cancel rval is %d
    ",(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
    ",(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
    ",(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
    ");
    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
    ");
    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
    ");
    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
    ");
    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
    ",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
    ",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
    ");
    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
    ");
    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
    ",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
    ",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
    ");    
    64         return 0;
    65         }
    66     err = pthread_create (&tid1, NULL, thread_fun1, NULL);
    67     if(err != 0)
    68     {
    69        printf("create new thread1 failure
    ");
    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
    ");
    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
    ",num);
     10     sleep(5);
     11     printf("thread1 is over
    ");
     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
    ",num);
     21     sleep(5);
     22     printf("thread2 is over
    ");
     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
    ");
     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
    ");
     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
    ");
     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
    ",num);
     78     sleep(5);
     79     printf("thread1 is over
    ");
     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
    ",num);
     89     sleep(5);
     90     printf("thread2 is over
    ");
     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
    ");
    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
    ");
    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
    ");
    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
    ");
     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
    ");
     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);
     89        put(&buffer ,n);
     90        printf("put the %d produte succes
    ",n);
     91        
     92    }
     93 
     94    printf("producer stopped
    ");
     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 ...
    ");
    107     num = get(&buffer);
    108     printf("get the %d product success
    ",num);
    109     if (++cnt == PRODUCT_CNT)
    110         break;
    111         
    112     }
    113    printf("consumer stopped
    ");    
    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
    ",num);
     8     return (void *)1;
     9 }
    10 
    11 void *thread_fun2(void *s)
    12 {
    13     printf("thread2 printf num %d
    ",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
    ");
    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
    ");
    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
    ",stacksize);
    11     pthread_attr_setstacksize(&attr , 16400);
    12         pthread_attr_getstacksize(&attr , &stacksize);
    13     printf("thread1 after stack size is  %d
    ",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
    ");
    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
    ");
    24 
    25         //将共享内存内容改为hello
    26         memcpy (buf , "hello" , 6);
    27         printf("child buf is : %s
    ",buf);
    28         
    29     }
    30     else if (pid >0)
    31     {
    32         printf("i an parent proccess
    ");    
    33         
    34         //修改内容为world
    35         memcpy(buf , "world",6);
    36         sleep(3);
    37         printf("parent buf is : %s
    ",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
    ");
    38 
    39         pthread_mutex_lock(mutex);
    40         //将共享内存内容改为hello
    41         memcpy (buf , "hello" , 6);
    42         printf("child buf is : %s
    ",buf);
    43         pthread_mutex_unlock(mutex);
    44     }
    45     else if (pid >0)
    46     {
    47         printf("i an parent proccess
    ");    
    48         
    49         pthread_mutex_lock(mutex);
    50         //修改内容为world
    51         memcpy(buf , "world",6);
    52         sleep(3);
    53         printf("parent buf is : %s
    ",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
    ");
     8     int a =1;
     9     pthread_setspecific(key , (void *)a);
    10     sleep(2);
    11     printf("thread1 key -> data is %d
    ", (int)pthread_getspecific(key));
    12     return (void *)1;
    13 }
    14 
    15 void *thread_fun2(void *s)
    16 {
    17     sleep(1);
    18     printf("thread2 start
    ");
    19     int a =2;
    20     pthread_setspecific(key , (void *)a);
    21     
    22     printf("thread2 key -> data is %d
    ",(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
    ");
    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
    ");
    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
    ");
    14     pthread_mutex_unlock(&mutex);
    15        
    16    }
    17    if(pid>0)
    18    {
    19     pthread_mutex_lock(&mutex);
    20     printf("parent process
    ");
    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
    ");
    32     return;
    33        
    34    }
    35    pthread_mutex_lock(&mutex);
    36    sleep(2);
    37    printf("main
    ");
    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
    ");
    15     pthread_mutex_unlock(&mutex);
    16     printf("child process
    ");
    17        
    18    }
    19    if(pid>0)
    20    {
    21 //    pthread_mutex_lock(&mutex);
    22 //    printf("parent process
    ");
    23     pthread_mutex_unlock(&mutex);
    24     printf("parent process
    ");
    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
    ");
    35     return;
    36        
    37    }
    38   // pthread_mutex_lock(&mutex);
    39   // sleep(2);
    40    printf("main
    ");
    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
    ");
     8 }
     9 
    10 void parent()
    11 {
    12    pthread_mutex_unlock(&mutex);
    13    printf("i am parent
    ");
    14     
    15 }
    16 
    17 void child()
    18 {
    19     pthread_mutex_unlock(&mutex);
    20     printf("i am child
    ");
    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
    ");
    33     pthread_mutex_unlock(&mutex);
    34        
    35    }
    36    if(pid>0)
    37    {
    38     pthread_mutex_lock(&mutex);
    39     printf("parent process
    ");
    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
    ");
    51     return;
    52        
    53    }
    54   // pthread_mutex_lock(&mutex);
    55   // sleep(2);
    56    printf("main
    ");
    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函数
  • 相关阅读:
    C#语法糖
    C#十种语法糖
    委托
    C#迭代器
    C#事件
    C#事件
    c# event 事件浅析
    ASP.NET CORE 增删改查
    asp.net core 增删改查
    asp.net core 搭建MVC
  • 原文地址:https://www.cnblogs.com/chu-yi/p/9843179.html
Copyright © 2020-2023  润新知