12.8 多线程
之前,总是让程序的主线程仅仅创建一个线程,这节将演示如何在同一个程序中创建多个线程,然后如何以不同于其启动顺序将它们合并在一起。此外,还 演示多线程编程时容易出现的时序问题.编写程序thread8.c
/*************************************************************************
> File Name: thread8.c
> Description: thread8.c程序创建多个线程,然后以不同于启动顺序将它们合并在一起
> Author: Liubingbing
> Created Time: 2015年07月07日 星期二 19时37分45秒
> Other: thread8.c程序存在一个小漏洞,如果主线程运行足够快时,可能修改传递引用的参数thread_index,造成问题.见thread8a.c
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 6
void *thread_function(void *arg);
int main(){
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int thread_index;
for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
/* pthread_create创建新线程,这里创建了一个线程ID的数组 */
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finish...\n");
/* 主线程中等待合并这些子线程,但并不是以创建它们的顺序来合并 */
for (thread_index = NUM_THREADS - 1; thread_index >= 0; thread_index--) {
res = pthread_join(a_thread[thread_index], &thread_result);
if (res == 0) {
printf("Picked up a thread\n");
} else {
perror("pthread_join failed");
}
}
printf("All done\n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int my_number = *(int *)arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
/* 创建的线程等待一段随机的时间退出运行 */
rand_num = 1 + (int)(9.0 * rand() / (RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
运行thread8.c,看到如下结果:
pthread_t a_thread[NUM_THREADS];
然后通过循环创建多个线程,如下所示:
for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
}
创建出的线程等待一段随机的时间后退出运行,如下所示:
void *thread_function(void *arg) {
int my_number = *(int *) arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
rand_num = 1 + (int)(9.0 * rand() / RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
在主线程中,等待合并这些子线程,但并不是以创建它们的顺序来合并,如下所示:
for (thread_index = NUM_THREADS -1; thread_index >= 0; thread_index--) {
res = pthread_join(a_thread[thread_index], &thread_result);
...
}
这个程序有一个小漏洞,如果将sleep调用从启动线程的循环中删除,它将会变得很明显。很可能会看到一些奇怪的现象,比如一些线程以相同的参数被启动,类似下图:
为什么会出现这样的问题?启动线程时,线程函数的参数是一个局部变量,这个变量在循环中被更新,引起问题的代码行是:
for (thread_index = 0; thread_index < NUM_THREADS; thread_index++) {
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)&thread_index);
}
如果主线程运行的足够快(因为删除sleep(1)之后,主线程相对新线程就非常快了),就可能改变某些线程的参数(即thread_index)。
此时,传递引用不是恰当的选择,而传值是正确的.当对共享变量和多个执行路径没有做到足够重视时,程序就可能出现这样的错误行为。编写线程程序时需要在设计上特别小心。要改正这个问题,可以直接传递给这个参数的值,如下所示:
res = pthread_create(&(a_thread[thread_index]), NULL, thread_function, (void *)thread_index);
还有修改thread_function函数,如下所示:
int my_number = (int) arg;