采用pthread_create建立一个新线程,与fork方式建立新进程不同(尽管在Linux环境下进程与线程具有相同概念)。所有pthread将会共用主线程中的所有变量,而不是如fork方式仅仅将所有变量引用加1。由于pthread共用问题的存在,将会引发在Linux下多线程编程的互斥问题。测试如下:
代码示例:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5 int counter = 0;
6 //pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
7
8 void *doit(void *arg);
9
10 int main(void)
11 {
12 pthread_t tida, tidb;
13
14 pthread_create(&tida, NULL, &doit, NULL);
15 pthread_create(&tidb, NULL, &doit, NULL);
16
17 pthread_join(tida, NULL);
18 pthread_join(tidb, NULL);
19
20 exit(0);
21 }
22
23 void *doit(void *arg)
24 {
25 int i, val;
26
27 for(i = 0;i < 100; i++) {
28 //pthread_mutex_lock(&counter_mutex);
29
30 val = counter;
31 printf("%d : %d\n", pthread_self(), val + 1);
32 counter = val + 1;
33
34 //pthread_mutex_unlock(&counter_mutex);
35 }
36
37 return NULL;
38 }
a与b线程执行doit函数,并将循环100次将共用变量counter加1。
输入结果如下:
截图1
截图2:
截图3:
两个线程号分别以20,24进行标识,我们把C编译器进行加运算符转换为3条机器指令,第一条是从内存中取数据装载到寄存器,第二条是递减寄存器,第三条是从寄存器取数据存储到内存。从截图可以看出执行结果为:
截图1中,20号线程首先运行,执行打印代码并执行counter = val + 1,当其运行到counter = 10(counter=9+1)时,被CPU剥夺执行权限,此时交由24号线程运行(20号线程等待运行printf代码),24号线程首次执行val = counter,i=0,并将当前val值赋值为10,CPU剥夺其执行权限,20号线程重新运行(24号线程等待运行printf代码),20号线程继续执行printf,打印输出结果一直到打印到19,CPU剥夺执行权限交由24号线程,24号线程执行printf,打印输出结果11(val+1)
截图2中,24号线程一直运行,直到结束都没有被CPU切出,但其初始值val从10开始,故最后一次打印结果为110,24号线程执行结束,返回。20号线程被CPU唤醒,执行printf,打印输出20
截图3中,20号线程一直运行,直到结束,返回。
从执行结果也可以看出,由于未加互斥操作,使得24号线程初始运行时,counter为10,而且在24号线程结束运行counter被置为109时,位于20号线程中counter没有及时被同步,使得20号线程继续从19开始递增。
为避免上述问题存在,添加互斥锁方式可以进行解决,将上述代码注释位置去掉。通过添加互斥锁的方式,线程在操作counter变量前锁住该互斥锁,如果试图对一个已经上锁的互斥锁上锁,则该线程将会被阻塞,知道互斥锁被解锁。最终输出结果,计数器将会单调递增,最终值为200。