当前位置: 移动技术网 > 科技>操作系统>Linux > Linux 多线程

Linux 多线程

2018年11月04日  | 移动技术网科技  | 我要评论

  

  

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 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网