1、 一个简单的 Linux 多线程例子 带你洞悉互斥量 信号量 条件变量编程一、什么是多线程?当我自己提出这个问题的时候,我还是很老实的拿着操作系统的书,按着上面的话敲下“为了减少进程切换和创建开销,提高执行效率和节省资源,我们引入了线程的概念,与进程相比较,线程是 CPU 调度的一个基本单位。” 形象点的举个例子说:一个 WEB 服务 器可以同时接收来自不同用户的网页访问请求,显然服务器处理这些网页请求都是通过并发进行的否则将会造成用户等待时间长或者响应效率低等问题。如果在服务器中使用进程的办法来处理来自不同网页访问请求的话,我们可以用创建父进程以及多个子进程的方法,然而这样会花费很大的系统开
2、销和占用较多的资源,因此这样会较大的限制了访问服务器的用户数量。使用多线程的理由之一是和进程相比,它是一种非常 “节俭“的多任务操作方式。我们知道,在 Linux 系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种“昂贵“的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然
3、,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改二、互斥锁正如上面所说的,如果两个线程同时对一块内存进行读写或者对向同一个文件写数据,那么结果是难以设想的。正因为如此,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“ 互斥锁“ 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。例如 int *a int *b 分别指向两块内存,上面的值分别初始化为(200 , 100) 线程 A 执行这样的一个操作:将*a 的值减少 50,*b
4、 的值增加 50.线程 B 执行:打印出(a 跟 b 指向的内存的值的和)。如果串行运行:A: *a -= 50; *b += 50; B: printf(“%dn“, *a + *b); 如果并发执行,则有可能会出现一下调度:*a -= 50; printf(“%dn“, *a + *b); *b += 50;因此我们可以引入互斥量,在对共享数据读写时进行锁操作,实现对内存的访问以互斥的形式进行。#include #include #include int a = 200;int b = 100;pthread_mutex_t lock;void* ThreadA(void*)pthread
5、_mutex_lock( /锁a -= 50;sleep(5); /执行到一半 使用 sleep 放弃 cpu 调度b += 50;pthread_mutex_unlock(void* ThreadB(void*)sleep(1); /放弃 CPU 调度 目的先让 A 线程运行。pthread_mutex_lock(printf(“%dn“, a + b);pthread_mutex_unlock(int main()pthread_t tida, tidb;pthread_mutex_init(pthread_create(pthread_create(pthread_join(tida,
6、NULL);pthread_join(tidb, NULL);return 1;以上输出为 300 去掉锁操作 输出为 250。三、信号量 作用域 上锁时信号量 进程间或线程间(linux 仅线程间)只要信号量的 value 大于 0,其他线程就可以 sem_wait 成功,成功后信号量的 value 减一。若 value 值不大于 0,则sem_wait 阻塞,直到 sem_post 释放后 value 值加一互斥锁 线程间 只要被锁住,其他任何线程都不可以访问被保护的资源 成功后否则就阻塞信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数 sem_
7、post()增加信号量。只有当信号量值大于时,才能使用公共资源,使用后,函数 sem_wait()减少信号量。信号量不一定是锁定某一个资源,而是流程上的概念,比如:有 A,B 两个线程,B 线程要等 A 线程完成某一任务以后再进行自己下面的步骤,这个任务并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。可能这样说大家对信号量的概念还是很模糊,举个例子,现在有个图书馆,其能容纳100 人,现在有两个线程 A、 B,A 线程执行:往图书管理进入一个人,B 线程:从图书馆出来一个人。那么为了使得线程 A 在图书馆满人的时候进入等待,而不是继续往图书馆里进人,使得 B 线程在图书馆没人的时
8、候等待人进入,我们可以引入信号量:IN OUT 分别初始化为100 和 0。那么 A 则可以被表示为 A:P(IN ) /剩余容量减少一 如果容量为 0 则等待 B:P(OUT) /人数减少 1 如果人数为 0 则阻塞等待. /登记进入图书馆的人信息 . /记录 离开图书馆的人信息 (随便一系列操作。)V(OUT)/增加信号量 OUT 表示人数+1 V(IN) /增加图书馆剩余容量+1通过这样我们就实现了线程的同步。sem_init 用语初始化一个信号量其原型: int sem_init(sem_t *sem, int pshared, unsigned int value);sem_init
9、() 初始化一个定位在 sem 的匿名信号量。 value 参数指定信号量的初始值。pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在所有线程都可见的地址上上面的 P V 操作相当于 sem_wait () sempost()sem_wait 函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为 2 的信号量调用sem_wait(),线程将会继续执行,这信号量的值将减到 1。如果对一个值为 0 的信号量调用sem
10、_wait(),这个函数就 会地等待直到有其它线程增加了这个值使它不再是 0 为止。如果有两个线程都在 sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_post 的作用很简单,就是使信号量增加 1三、条件变量条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条
11、件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。假设有共享的资源 sum,与之相关联的 mutex 是 lock_s.假设每个线程对 sum 的操作很简单的,与 sum 的状态无关,比如只是 sum+.那么只用 mutex 足够了.程序员只要确保每个线程操作前,取得 lock,然后 sum+,再 unlock 即可.每个线程的代码将像这样add() pthread_mutex_lock(lock_s); sum+; pthread_mutex_unlock(lock_s); 如果操作比较复杂,假设线程 t0,t1,t2 的操作是 su
12、m+,而线程 t3 则是在 sum 到达 100的时候,打印出一条信息,并对 sum 清零. 这种情况下,如果只用 mutex, 则 t3 需要一个循环,每个循环里先取得 lock_s,然后检查 sum 的状态,如果 sum=100,则打印并清零,然后unlock.如果 sum amp; amp; lt;100,则 unlock,并 sleep()本线程合适的一段时间.这个时候,t0,t1,t2 的代码不变,t3 的代码如下:print() while pthread_mutex_lock(lock_s); if(sum=100) printf(“sum reach 100!”); pthre
13、ad_mutex_unlock(lock_s); else pthread_mutex_unlock(lock_s); my_thread_sleep(100); return ; 这种办法有两个问题 1) sum 在大多数情况下不会到达 100,那么对 t3 的代码来说,大多数情况下,走的是 else 分支,只是 lock 和 unlock,然后 sleep().这浪费了 CPU 处理时间. 2) 为了节省 CPU 处理时间,t3 会在探测到 sum 没到达 100 的时候 sleep()一段时间.这样却又带来另外一个问题,亦即 t3 响应速度下降.可能在 sum 到达 200 的时候,t3
14、 才醒过来. 3) 这样,程序员在设置 sleep()时间的时候陷入两难境地,设置得太短了节省不了资源 ,太长了又降低响应速度.真是难办啊!这个时候,condition variable 内裤外穿,从天而降, 拯救了焦头烂额的你. (抄袭的哈哈)你首先定义一个 condition variable. pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER; t0,t1,t2 的代码只要后面加两行,像这样add() pthread_mutex_lock(lock_s); sum+; pthread_mutex_unlock(lock_s);
15、if(sum=100) pthread_cond_signal( T3 线程的 printprint pthread_mutex_lock(lock_s); while(sum注意两点: 1) 在 thread_cond_wait()之前,必须先 lock 相关联的 mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会 unlock 该 mutex, 然后 block,在目标条件满足后再重新lock 该 mutex, 然后返回. 2) 为什么是 while(sum#include #include #include #includepthread_mutex_t
16、 Poll_Work; /互斥锁sem_t Poll_IN; /水池容量信号量 sem_t Poll_OUT; /当前水量信号量void* thread0(void *param) /0 1 线程往水池中加水。while(1)int rv = 0;for(int i = 0; i extern void pthread_exit _P (void *_retval) /函数的返回代码(3)等待一个线程的结束#include extern int pthread_join _P (pthread_t _th, /被等待的线程标识符void *_thread_return);/ 一个用户定义的指针
17、,它可以用来存储被等待线程的返回值这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。下面在 linux 环境下编写 :/*threadtest.c*/#include #include #include #include /*声明线程运行服务程序*/static void pthread_func_1 (void); static void pthread_func_2 (void); int main (void) /*线程的标识符*/pthread_t pt_1 = 0; pthread_t pt_2 = 0; int ret
18、 = 0; /*分别创建线程 1、2*/ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_1_create“); ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_2_create“); /*等待线程 1、2 的结束*/pthread_join (pt_1, NULL); pthread_join (pt_2, NULL); /*主线程退出*/printf (“main programme exit!/n“); return 0; /*线程 1 的服务程序*/st
19、atic void pthread_func_1 (void) int i = 0; for (; i pthread_attr_t attr;pthread_t tid;/*初始化属性值,均设为默认值*/pthread_attr_init(pthread_attr_setscope(pthread_create(属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省 1M 的堆栈、与父进程同样级别的优先级。关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间
20、。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的“绑“ 在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为 CPU 时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当 pt
21、hread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常
22、快,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用 pthread_create 的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create 返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如 wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。另外一个可能常用的属性是线程的优先级,它存放在结构 sche
23、d_param 中。用函数pthread_attr_getschedparam 和函数 pthread_attr_setschedparam 进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。再看常用的几个函数:1、pthread_attr_init功能: 对线程属性变量的初始化。头文件: 函数原型: int pthread_attr_init (pthread_attr_t* attr);函数传入值:attr:线程属性。函数返回值:成功: 0失败: -12、pthread_attr_setscope功能: 设置线程绑定属性。头文件: 函数原型: int pthread_at
24、tr_setscope (pthread_attr_t* attr, int scope);函数传入值:attr: 线程属性。scope:PTHREAD_SCOPE_SYSTEM(绑定)PTHREAD_SCOPE_PROCESS(非绑定)函数返回值得:同 1。3、pthread_attr_setdetachstate功能: 设置线程分离属性。头文件: 函数原型: int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);函数传入值:attr:线程属性。detachstate:PTHREAD_CREATE_DE
25、TACHED(分离)PTHREAD_CREATE_JOINABLE(非分离)函数返回值得:同 1。4、pthread_attr_getschedparam功能: 得到线程优先级。头文件: 函数原型: int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);函数传入值:attr:线程属性;param:线程优先级;函数返回值:同 1。5、pthread_attr_setschedparam功能: 设置线程优先级。头文件: 函数原型: int pthread_attr_setschedparam
26、 (pthread_attr_t* attr, struct sched_param* param);函数传入值:attr:线程属性。param:线程优先级。函数返回值:同 1。然后编写一个程序练习 :/*threadtest2.c*/#include #include #include #include /*声明线程运行服务程序*/static void pthread_func_1 (void); static void pthread_func_2 (void); int main (void) /*线程的标识符*/pthread_t pt_1 = 0; pthread_t pt_2 =
27、 0; int ret = 0; pthread_attr_t attr = 0; /属性结构/*属性设置 */pthread_attr_init ( /初始化属性 pthread_attr_setscope ( /绑定pthread_attr_setdetachstate ( /分离/*分别创建线程 1、2*/ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_1_create“); ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_2_create“); /*等
28、待线程 2 的结束*/ pthread_join (pt_2, NULL); sleep(2);/注意线程 2 结束时线程 1 没结束,这里不调用 sleep 会看不到printf (“main programme exit!/n“); return 0; /*线程 1 的服务程序*/static void pthread_func_1 (void) int i = 0; for (; i #include #include #include /*全局变量*/int gnum = 0;/*互斥量 */pthread_mutex_t mutex;/*声明线程运行服务程序*/static void
29、 pthread_func_1 (void); static void pthread_func_2 (void); int main (void) /*线程的标识符*/pthread_t pt_1 = 0; pthread_t pt_2 = 0; int ret = 0; /*互斥初始化*/pthread_mutex_init ( /*分别创建线程 1、2*/ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_1_create“); ret = pthread_create ( /无参数if (ret != 0) perror
30、(“pthread_2_create“); /*等待线程 1、2 的结束*/pthread_join (pt_1, NULL); pthread_join (pt_2, NULL); printf (“main programme exit!/n“); return 0; /*线程 1 的服务程序*/static void pthread_func_1 (void) int i = 0; for (;) printf (“This is pthread1!/n“); pthread_mutex_lock( /*获取互斥锁*/*注意,这里以防线程的抢占,以造成一个线程在另一个线程 sleep 时
31、多次访问互斥资源,所以 sleep 要在得到互斥锁后调用*/sleep (1); /*临界资源 */gnum+;printf (“Thread1 add one to num:%d/n“,gnum);pthread_mutex_unlock( /*释放互斥锁*/ /*线程 2 的服务程序*/ static void pthread_func_2 (void) int i = 0; for (;) printf (“This is pthread2!/n“); pthread_mutex_lock( /*获取互斥锁*/*注意,这里以防线程的抢占,以造成一个线程在另一个线程 sleep 时多次访问
32、互斥资源,所以 sleep 要在得到互斥锁后调用*/sleep (1);/*临界资源 */gnum+;printf (“Thread2 add one to num:%d/n“,gnum);pthread_mutex_unlock( /*释放互斥锁*/ pthread_exit (0); 然后编译,运行,看到是线程 1,2 分别和平地访问共享资源。linux多线程编程 4-条件变量上一篇练习了互斥锁的用法和原理,这次学习和互斥锁一起应用的 cond-条件变量1.互斥锁的存在问题:互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。设想一种简单情景:多个线程访问同一个共享资源时,并不知道何时应该
33、使用共享资源,如果在临界区里加入判断语句,或者可以有效,但一来效率不高,二来复杂环境下就难以编写了,这是我们需要一个结构,能在条件成立时触发相应线程,进行变量修改和访问。2.条件变量:条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。3.条件变量的相关函数头文件:#include pthread_cond_t c
34、ond = PTHREAD_COND_INITIALIZER; /条件变量结构int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t*cond_attr);int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);int pthread_cond_timedwa
35、it(pthread_cond_t *cond, pthread_mutex_t*mutex, const struct timespec *abstime);int pthread_cond_destroy(pthread_cond_t *cond);详细说明:(1)创建和注销条件变量和互斥锁一样,都有静态动态两种创建方式a.静态方式静态方式使用 PTHREAD_COND_INITIALIZER 常量,如下:pthread_cond_t cond=PTHREAD_COND_INITIALIZERb.动态方式动态方式调用 pthread_cond_init()函数,API 定义如下:int p
36、thread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)尽管 POSIX 标准中为条件变量定义了属性,但在 LinuxThreads 中没有实现,因此cond_attr 值通常为 NULL,且被忽略。注销一个条件变量需要调用 pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回 EBUSY。因为 Linux 实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API 定义如下:int pthread_cond_destroy(pthr
37、ead_cond_t *cond)(2)等待和激发a.等待int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)/等待int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)/有时等待等待条件有两种方式:无条件等待 pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回E
38、TIMEOUT,结束等待,其中 abstime 以与 time()系统调用相同意义的绝对时间形式出现,0 表示格林尼治时间 1970 年 1 月 1 日 0 时 0 分 0 秒。无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或 pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex 互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用 pthread_cond_wait()前必须由本线程加锁(p
39、thread_mutex_lock()),而在更新条件等待队列以前,mutex 保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前,mutex将被重新加锁,以与进入 pthread_cond_wait()前的加锁动作对应。b.激发激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而 pthread_cond_broadcast()则激活所有等待线程。(3) 其他操作pthread_cond_wait ()和 pthread_cond_timedwait()都被实
40、现为取消点,因此,在该处等待的线程将立即重新运行,在重新锁定 mutex 后离开 pthread_cond_wait(),然后执行取消动作。也就是说如果 pthread_cond_wait()被取消,mutex 是保持锁定状态的,因而需要定义退出回调函数来为其解锁。看完基本操作后做一个小练习:建立两个线程 1、2 ,两个线程分别访问共享资源,并进行加 1 操作,当共享资源3 后,两者都工作/*condmutex.c*/#include #include #include #include /*全局变量*/int gnum = 0;/*互斥量 */pthread_mutex_t mutex;/*
41、条件变量*/pthread_cond_t cond;/*声明线程运行服务程序*/static void pthread_func_1 (void); static void pthread_func_2 (void); int main (void) /*线程的标识符*/pthread_t pt_1 = 0; pthread_t pt_2 = 0; int ret = 0; /*互斥初始化*/pthread_mutex_init ( /*条件变量初始化*/pthread_cond_init(/*分别创建线程 1、2*/ret = pthread_create ( /无参数if (ret !=
42、0) perror (“pthread_1_create“); ret = pthread_create ( /无参数if (ret != 0) perror (“pthread_2_create“); /*等待线程 1、 2 的结束*/pthread_join (pt_1, NULL); pthread_join (pt_2, NULL); printf (“main programme exit!/n“); return 0; /*线程 1 的服务程序*/static void pthread_func_1 (void) int i = 0; for (;) printf (“This i
43、s pthread1!/n“); pthread_mutex_lock( /*获取互斥锁*/*注意,这里以防线程的抢占,以造成一个线程在另一个线程 sleep 时多次访问互斥资源,所以 sleep 要在得到互斥锁后调用*/sleep (1);/*条件变量,当 gnum函数原型: int sem_init (sem_t* sem, int pshared, unsigned int value);函数传入值: sem:信号量。pshared:决定信号量能否在几个进程间共享。由于目前 LINUX 还没有实现进程间共享信息量,所以这个值只能取 0。value:初始计算器函数返回值: 0:成功。-1:
44、失败。(2)其他函数。/等待信号量int sem_wait (sem_t* sem);int sem_trywait (sem_t* sem);/发送信号量int sem_post (sem_t* sem);/得到信号量值int sem_getvalue (sem_t* sem);/删除信号量int sem_destroy (sem_t* sem);功能:sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量的值小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。sem_post 相当于 V 操作,它将信
45、号量的值加一,同时发出唤醒的信号给等待的进程(或线程)。sem_getvalue 得到信号量的值。sem_destroy 摧毁信号量。函数传入值: sem:信号量。函数返回值: 同上。好了,了解完基本操作,继续做一个练习:这里用信号量实现互斥资源访问的功能:/*sem.c*/ #include #include #include #include #include /*全局变量*/ int gnum = 0; /*信号量*/ sem_t sem; /*声明线程运行服务程序*/ static void pthread_func_1 (void); static void pthread_func
46、_2 (void); int main (void) /*线程的标识符*/ pthread_t pt_1 = 0; pthread_t pt_2 = 0; int ret = 0; /*信号量初始化*/ sem_init( /*分别创建线程 1、2*/ ret = pthread_create ( /无参数 if (ret != 0) perror (“pthread_1_create“); ret = pthread_create ( /无参数 if (ret != 0) perror (“pthread_2_create“); /*等待线程 1、2 的结束*/ pthread_join (
47、pt_1, NULL); pthread_join (pt_2, NULL); printf (“main programme exit!/n“); return 0; /*线程 1 的服务程序*/ static void pthread_func_1 (void) int i = 0; for (;) printf (“This is pthread1!/n“); sem_wait( /*等待信号量*/ sleep (1); /*临界资源 */ gnum+; printf (“Thread1 add one to num:%d/n“,gnum); sem_post ( /*释放信号量*/ /*线程 2 的服务程序*/ static void pthread_func_2 (void) int i = 0; for (;) printf (“This is pthread2!/n“); sem_wait( /*等待信号量*/ sleep (1); /*临界资源 */ gnum+; printf (“Thread2 add one to num:%d/n“,gnum); sem_post ( /*释放信号量*/ pthread_exit (0);