一、描述
进程间的同步。具有同步关系的一组并发进程,称为合作进程,合作进程间互相发送的信号,称为消息或事件。
这种需要进程间同步的情况,是可以想见的,例如几个进程访问“临界资源”。而为了解决进程间的同步问题,引入信号量的概念。
二、异步执行
所谓异步执行命令,就是说一个线程用于接收解析命令,另外一个线程用于实际执行命令。实际工程中,经常会遇到有许多种命令要在一个线程中得到解析并执行,有些命令耗时短,可以在这个线程中完成;但是,有些命令耗时长,如果也放在这个线程中,则影响该线程接收(其他命令)。所以,此时可以考虑用异步执行的方案,将耗时短的命令,就放在接收解析线程中;而将耗时长的命令,则用异步执行的方案,将接收与实际执行分离,以避免接收线程受到严重阻塞。
example:
本例程中,用主线程创建了两个子线程pthread1和pthread2,其中线程pthread1用于产生命令(模仿接受解析过程),而线程pthread2用于实际执行命令。
代码如下:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<pthread.h>
4 #include <semaphore.h>
5
6 /* 将信号量定义为全局变量,方便多个线程共享 */
7 sem_t sem;
8
9 /* 线程1和线程2的公用命令 */
10 int gCmd = 0;
11
12 /* 同步线程1和线程2的全局变量 */
13 static int gIsExecFlag = 0;
14
15 /* 定义线程pthread1 */
16 static void * pthread1(void *arg)
17 {
18 /* 线程pthread1开始运行 */
19 printf("pthread1 start!\n");
20
21 while(1)
22 {
23 /* 等待没有命令正在执行 */
24 while(gIsExecFlag);
25
26 /* 更新命令 */
27 gCmd++;
28 if(gCmd == 10)
29 {
30 /* 释放信号量 */
31 sem_post(&sem);
32
33 /* 发送命令结束 */
34 return NULL;
35 }
36
37 /* 释放信号量 */
38 sem_post(&sem);
39
40 /* 等待线程2执行命令 */
41 sleep(1);
42 }
43 }
44
45 /* 定义线程pthread2 */
46 static void * pthread2(void *arg)
47 {
48 int tmp;
49
50 /* 线程pthread2开始运行 */
51 printf("pthread2 start!\n");
52
53 while(1)
54 {
55 if (sem_wait(&sem) != 0)
56 {
57 printf("Error!\n");
58 }
59
60 /* 正在执行的标志置1 */
61 gIsExecFlag = 1;
62
63 /* 线程2接受来自线程1的命令,并打印 */
64 tmp = gCmd;
65 printf("now execute the cmd,and the code of cmd is %d.\n", tmp);
66
67 /* 执行命令需要时间:3s,模仿实际命令执行 */
68 sleep(3);
69
70 /* 正在执行的标志清0 */
71 gIsExecFlag = 0;
72
73 if(gCmd == 10){
74 /* 命令执行结束 */
75 return NULL;
76 }
77 }
78 }
79
80 /* main函数 */
81 int main(int agrc,char* argv[])
82 {
83 pthread_t tidp1,tidp2;
84
85 /* 初始化信号量sem,注意初始值为0 */
86 sem_init(&sem, 0, 0);
87
88 /* 创建线程pthread1 */
89 if ((pthread_create(&tidp1, NULL, pthread1, NULL)) == -1)
90 {
91 printf("create error!\n");
92 return 1;
93 }
94
95 /* 同步,让线程1先执行 */
96 usleep(10);
97
98 /* 创建线程pthread2 */
99 if ((pthread_create(&tidp2, NULL, pthread2, NULL)) == -1)
100 {
101 printf("create error!\n");
102 return 1;
103 }
104
105 /* 等待线程pthread1释放 */
106 if (pthread_join(tidp1, NULL))
107 {
108 printf("thread is not exit...\n");
109 return -2;
110 }
111
112 /* 等待线程pthread2释放 */
113 if (pthread_join(tidp2, NULL))
114 {
115 printf("thread is not exit...\n");
116 return -2;
117 }
118
119 return 0;
120 }
代码重点解析:
进程pthread1和进程pthread2之间单纯用信号量sem同步,无法解决发送线程pthread1,在线程pthread2正在执行命令时,又写入了新的命令的问题,造成命令执行错乱。为了解决这个问题,引入全局变量gIsExecFlag用于同步。经过信号量sem和全局变量gIsExecFlag的完美配合,就可以实现命令发送与执行过程的有序配合。
测试效果
编译命令:
#arm-linux-gcc -o pthread pthread.c -lpthread
执行结果:
后续分析
由上图可知,两个线程的整体执行周期,并非是线程pthread1和线程pthread2周期的和,而是取两者中的最大者。实际上,这也很容易想见,两个线程的通信速度,取决于两个线程中速度最慢者,也对应这个结论。
经过测试,不论发送线程和执行线程的速度孰大孰小,总体的执行结果是一样的,都能保证命令执行流程的正确。所以,就可以证明上述代码的可行性。但是,需要注意的是,线程之间的同步时间还是有限制的,线程pthread1的睡眠时间应≥10ms,否则将会出现执行流程的错误。