转载自 http://blog.csdn.net/jxhnuaa/article/details/3254299
1.我的系统是否支持多线程
如何知道我的系统是否支持多线程呢(POSIX标准)?有两种方法,一种方法是检查系统中的某些重要的头文件,如limits.h、unistd.h等;另外一种方法就是写一个简单的程序测试一下。由于前者比较复杂,所以一般采用后面的方法,即在程序中判断宏_POSIX_VERSION:
l 如果没有定义宏_POSIX_VERSION,那么肯定不支持多线程;
l 如果宏_POSIX_VERSION小于199506L,那么部分支持多线程;
l 如果宏_POSIX_VERSION大于等于199506L,那么完全支持多线程;
注意:有些情况下_POSIX_VERSION的取值不在支持多线程的范围内是编译器引起的,需要在编译选项中增加_POSIX_C_SOURCE的定义,如cc -D_POSIX_C_SOURCE=199506L xxx.c -o xxx,其中xxx表示待编译的文件名。
2.如何创建线程
2.1预置条件
在使用相关函数创建一个线程之前,有三件事要做:
l 定义宏_REENTRANT;
l 包含头文件<pthread.h>;
l 在编译器链接选项中添加-lpthread,从而将多线程库文件链接进来;
宏_REENTRANT需要在使用#include命令包含任何头文件之前定义,作用是通知编译器本程序调用的库函数都需要拥有RE-ENTRANT特性。拥有RE-ENTRANT特性的代码能够被多个线程同时调用或者嵌套调用(例如以前的errno变量就不具有RE-ENTRANT特性)。
2.2相关函数
pthread_create函数
pthread_create函数的作用是创建一个线程,其原型如下:
int pthread_create(pthread_t *thread, pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
参数thread:如果创建线程成功,标识本线程的唯一标识符通过本变量返回给函数调用者;
参数attr:调用者通过该参数描述期望创建的线程具有什么样的属性,传入NULL表示使用默认属性;
参数start_routine:线程的运行实体;
参数arg:传递给线程实体的参数;
返回值:0成功,非0为错误码;
pthread_exit函数
pthread_exit函数的作用是结束本线程,类似于进程中的exit函数,其原型如下:
void pthread_exit(void *retval);
参数retval:用于向其他线程传递数据;
pthread_join函数
pthread_join函数的作用是等待另外某一个线程结束,其原型如下:
int pthread_join(pthread_t th, void **thread_return);
参数th:描述等待哪一个线程结束,其取值为pthread_create函数第一个参数的返回值;
参数thread_return:被等待线程结束时的返回值,等于pthread_exit函数的参数;
2.3一个例子
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, it returned %s/n", (char *)thread_result);
printf("Message is now %s/n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep(3);
strcpy(message, "Bye!");
pthread_exit("Thank you for the CPU time");
}
程序的文件名为thread_example.c,下面开始编译和链接:
$ cc -D_REENTRANT thread_example.c -o thread_example -lpthread
程序运行结果如下:
$ ./ thread_example
Waiting for thread to finish...
thread_function is running. Argument was Hello World
Thread joined, it returned Thank you for the CPU time
Message is now Bye!
3.设置线程的属性
pthread_create函数的第二参数为指向pthread_attr_t结构的指针,设置为NULL表示使用默认属性创建一个线程。事实上,用户完全可以通过填写该结构来创建具有相关属性的线程,POSIX提供了一系列填写该结构的函数,从而设置所有相关属性,下面介绍一些比较常用的函数。
3.1初始化属性变量、释放属性变量
pthread_attr_init函数
pthread_attr_init函数的作用是初始化pthread_attr_t结构,用户在调用其他函数设置结构中的参数前,必须先调用本函数对结构进行初始化,其原型为:
int pthread_attr_init(pthread_attr_t *attr);
参数attr:待初始化的pthread_attr_t结构;
返回值:0,成功,非0,失败;
pthread_attr_destroy函数
pthread_attr_destroy函数的作用是释放pthread_attr_t结构占用的资源,pthread_attr_t结构经过初始化和其他函数设置后可能会占用某些资源,本函数的作用就是释放这些资源,一般在pthread_attr_t结构使用完毕后调用本函数,其原型为:
void pthread_attr_destroy(pthread_attr_t *attr);
参数attr:待释放的pthread_attr_t结构;
返回值:0,成功,非0,失败;
3.2设置或获取detachedstate属性
pthread_attr_setdetachstate函数
pthread_attr_setdetachstate函数的作用是设置线程的detachedstate属性,可以取值PTHREAD_CREATE_JOINABLE和PTHREAD_CREATE_DETACHED,前者是默认值,表示其他线程可以使用pthread_join函数等待本线程结束,后者表示其他线程不可以对本线程使用pthread_join,其原型为:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int state);
参数attr:待设置的pthread_attr_t结构;
参数state:取值PTHREAD_CREATE_JOINABLE或PTHREAD_CREATE_DETACHED,表示期望的属性值;
返回值:0成功,非0失败;
pthread_attr_getdetachstate函数
pthread_attr_getdetachstate函数的作用是获取pthread_attr_t结构中的detachedstate属性,原型如下:
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
参数attr:待获取属性的pthread_attr_t结构;
参数detachstate:属性的返回值;
返回值:0成功,非0失败;
3.3设置或获取调度算法属性
pthread_attr_setschedpolicy函数
pthread_attr_setschedpolicy函数的作用是设置schedpolicy属性,即线程调度算法。schedpolicy属性值可以是SCHED_RR、SCHED_FIFO、SCHED_OTHER,其中SCHED_RR表示轮训调度,SCHED_FIFO表示先进先出调度,SCHED_OTHER表示其他。拥有管理员权限的进程才可以创建具有SCHED_RR或SCHED_FIFO调度算法的线程,一般线程的默认调度算法都是SCHED_OTHER。函数原型为:
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
参数attr:待设置的pthread_attr_t结构;
参数policy:取值SCHED_RR、SCHED_FIFO或SCHED_OTHER;
返回值:0成功,非0失败;
pthread_attr_getschedpolicy函数
pthread_attr_getschedpolicy函数的作用是获取pthread_attr_t结构中的schedpolicy属性,原型如下:
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
参数attr:待获取属性的pthread_attr_t结构;
参数detachstate:属性的返回值;
返回值:0成功,非0失败;
pthread_attr_setschedparam函数
pthread_attr_setschedparam函数的作用是设置某调度算法下的具体调度参数,原型如下:
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
参数attr:待设置的pthread_attr_t结构;
参数param:调度参数,具体参见相关手册;
返回值:0成功,非0失败;
pthread_attr_getschedparam函数
pthread_attr_getschedparam函数的作用是获取pthread_attr_t结构中的调度参数,原型如下:
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
参数attr:待获取属性的pthread_attr_t结构;
参数param:调度参数的返回值;
返回值:0成功,非0失败;
pthread_attr_setinheritsched函数
pthread_attr_setinheritsched函数的作用是设置线程调度算法的继承特性,可以取值PTHREAD_EXPLICIT_SCHED或PTHREAD_INHERIT_SCHED,前者表示使用结构pthread_attr_t指定的调度算法,后者表示继承父线程使用的调度算法,默认为前者,原型如下:
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
参数attr:待设置的pthread_attr_t结构;
参数policy:取值PTHREAD_EXPLICIT_SCHED或PTHREAD_INHERIT_SCHED;
返回值:0成功,非0失败;
pthread_attr_getinheritsched函数
pthread_attr_getinheritsched函数的作用是获取pthread_attr_t结构中的继承属性,原型如下:
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
参数attr:待获取属性的pthread_attr_t结构;
参数inherit:继承属性的返回值;
返回值:0成功,非0失败;
pthread_attr_setscope函数
pthread_attr_setscope函数的作用是设置线程的在什么范围内竞争CPU资源,可以取值PTHREAD_SCOPE_SYSTEM或PTHREAD_SCOPE_PROCESS,前者表示在整个系统内竞争CPU资源,后者表示在同一进程内竞争CPU资源,默认为前者,原型如下:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
参数attr:待设置的pthread_attr_t结构;
参数policy:取值PTHREAD_SCOPE_SYSTEM或PTHREAD_SCOPE_PROCESS;
返回值:0成功,非0失败;
pthread_attr_getscope函数
pthread_attr_getscope函数的作用是获取pthread_attr_t结构中的CPU竞争范围属性,原型如下:
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
参数attr:待获取属性的pthread_attr_t结构;
参数scope:竞争范围的返回值;
返回值:0成功,非0失败;
3.4设置或获取线程栈的大小
某些系统可能不支持设置或获取线程栈,只有定义了_POSIX_THREAD_ATTR_STACKSIZE宏的系统才支持上述功能,具体由两个函数提供:
pthread_attr_setstacksize函数
pthread_attr_setstacksize函数的作用是设置线程栈的大小,单位是字节,在默认情况下线程的栈是比较大的,原型如下:
int pthread_attr_setstacksize(pthread_attr_t *attr, int size);
参数attr:待设置的pthread_attr_t结构;
参数size:栈的大小,单位为字节;
返回值:0成功,非0失败;
pthread_attr_getstacksize函数
pthread_attr_getstacksize函数的作用是获取pthread_attr_t结构中的栈的大小,原型如下:
int pthread_attr_getstacksize(const pthread_attr_t *attr, int *size);
参数attr:待获取属性的pthread_attr_t结构;
参数size:栈大小的返回值;
返回值:0成功,非0失败;
3.5一个例子
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int thread_finished = 0;
int main() {
int res;
pthread_t a_thread;
pthread_attr_t thread_attr;
struct sched_param scheduling_value;
res = pthread_attr_init(&thread_attr);
if (res != 0) {
perror("Attribute creation failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != 0) {
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER);
if (res != 0) {
perror("Setting scheduling policy failed");
exit(EXIT_FAILURE);
}
scheduling_value.sched_priority = sched_get_priority_min(SCHED_OTHER);
res = pthread_attr_setschedparam(&thread_attr, &scheduling_value);
if (res != 0) {
perror("Setting scheduling priority failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, &thread_attr, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
(void)pthread_attr_destroy(&thread_attr);
while(!thread_finished) {
printf("Waiting for thread to say it's finished.../n");
sleep(1);
}
printf("Other thread finished, bye!/n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
printf("thread_function is running. Argument was %s/n", (char *)arg);
sleep(4);
printf("Second thread setting finished flag, and exiting now/n");
thread_finished = 1;
pthread_exit(NULL);
}
运行结果:
Waiting for thread to say it's finished...
thread_function is running. Argument was Hello World
Waiting for thread to say it's finished...
Waiting for thread to say it's finished...
Waiting for thread to say it's finished...
Second thread setting finished flag, and exiting now
Other thread finished, bye!
4.如何关闭线程
一个线程关闭另外一个线程一般有两种方法:发送一个事件给另外一个线程,让它自己调用pthread_exit结束;或者直接调用pthread_cancel函数关闭另外一个线程。这里介绍后面一种方法。
4.1相关函数
pthread_cancel函数
pthread_cancel函数的作用是关闭另外一个线程,原型如下:
int pthread_cancel(pthread_t thread);
thread参数:线程标识,描述关闭哪一个线程;
返回值:0成功,非0失败;
pthread_setcancelstate函数
pthread_setcancelstate函数的作用是设置本线程是否允许被其他线程关闭,原型如下:
int pthread_setcancelstate(int state, int *oldstate);
参数state:可以取值PTHREAD_CANCEL_ENABLE或PTHREAD_CANCEL_DISABLE,前者表示允许被其他线程关闭,后者表示不允许,默认为前者;
参数oldstate:返回设置之前的本属性值;
返回值:0成功,非0失败;
pthread_setcanceltype函数
pthread_setcanceltype函数的作用是设置本线程被其他线程关闭时,以什么方式关闭,原型如下:
int pthread_setcanceltype(int type, int *oldtype);
参数type:PTHREAD_CANCEL_ASYNCHRONOUS或PTHREAD_CANCEL_DEFERRED,前者表示被立刻关闭,后者表示等线程被阻塞时再关闭,默认为后者;
参数oldtype:返回设置之前的本属性值;
返回值:0成功,非0失败;
4.2一个例子
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(3);
printf("Cancelling thread.../n");
res = pthread_cancel(a_thread);
if (res != 0) {
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int i, res;
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0) {
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != 0) {
perror("Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}
printf("thread_function is running/n");
for(i = 0; i < 10; i++) {
printf("Thread is still running (%d).../n", i);
sleep(1);
}
pthread_exit(0);
}
运行结果:
thread_function is running
Thread is still running (0)...
Thread is still running (1)...
Thread is still running (2)...
Cancelling thread...
Waiting for thread to finish...
Thread is still running (3)...
5.线程中的数据同步
在多线程系统中,如果多个线程需要操作同一个变量,那么数据同步是必不可少的。UNIX/LINUX系统提供了两种方法来实现线程间的数据同步:旗语和互斥量。
5.1通过旗语同步
旗语同步是通过旗语变量及两个相关的原子操作来完成的,具体包含下面的四个函数。
sem_init函数
sem_init函数的作用是生成一个旗语变量,并初始化该变量,原型为:
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数sem:返回指向旗语变量的指针;
参数pshared:0用于本进程内,非0用于进程间,所以在线程间使用时一般设置为0;
参数value:旗语的初始值;
返回值:0成功,非0失败;
sem_wait函数
sem_wait函数是一个原子操作,如果旗语变量大于0,则将旗语变量减1,然后立刻返回;如果旗语变量小于等于0,则将线程阻塞,直到旗语变量大于0,原型为:
int sem_wait(sem_t * sem);
参数sem:指向待操作的旗语变量;
返回值:0成功,非0失败;
sem_post函数
sem_post函数也是一个原子操作,将旗语变量加1,如果旗语变量大于0,则唤醒对应的阻塞线程,原型为:
int sem_post(sem_t * sem);
参数sem:指向待操作的旗语变量;
返回值:0成功,非0失败;
sem_destroy函数
sem_destroy函数的作用是释放一个旗语变量,原型为:
int sem_destroy(sem_t * sem);
参数sem:指向待释放的旗语变量;
返回值:0成功,非0失败,如果有线程阻塞在该旗语变量上,释放函数会失败;
下面通过一个例子来解释旗语在线程中的应用:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
if (res != 0) {
perror("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish/n");
while(strncmp("end", work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&bin_sem);
}
printf("/nWaiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined/n");
sem_destroy(&bin_sem);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sem_wait(&bin_sem);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters/n", strlen(work_area) -1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}
程序运行结果如下:
Input some text. Enter 'end' to finish
The Wasp Factory
You input 16 characters
Iain Banks
You input 10 characters
end
Waiting for thread to finish...
Thread joined
5.2通过互斥量同步
互斥量(MUTEX)的方法是使用锁的概念。线程在使用临界资源前先对某个互斥量加锁,使用完毕后再对该互斥量解锁,这样就可以防止多个线程同时操作临界资源。具体包括四个函数:
pthread_mutex_init函数
pthread_mutex_init函数的作用是生成一个互斥量,原型为:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
参数mutex:返回指向互斥量的指针;
参数mutexattr:设置互斥量的属性,NULL表示使用默认属性,在默认属性下,系统不会检查是否存在死锁,例如同一个线程连续两次调用上锁函数就会发生死锁;
返回值:0成功,非0失败;
pthread_mutex_lock函数
pthread_mutex_lock函数的作用是对一个互斥量上锁,原型为:
int pthread_mutex_lock(pthread_mutex_t *mutex));
参数mutex:指向待上锁的互斥量;
返回值:0成功,非0失败;
pthread_mutex_unlock函数
pthread_mutex_unlock函数的作用是对一个互斥量解锁,原型为:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数mutex:指向待解锁的互斥量;
返回值:0成功,非0失败;
pthread_mutex_destroy函数
pthread_mutex_destroy函数的作用是释放一个互斥量,原型为:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex:指向待释放的互斥量;
返回值:0成功,非0失败;
下面通过一个例子解释互斥量在多线程中的应用:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter 'end' to finish/n");
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '/0') {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf("/nWaiting for thread to finish.../n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined/n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters/n", strlen(work_area) -1);
work_area[0] = '/0';
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == '/0' ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = '/0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
运行结果如下:
Input some text. Enter 'end' to finish
Whit
You input 4 characters
The Crow Road
You input 13 characters
end
Waiting for thread to finish...
Thread joined