一、C/C++多线程操作说明
C/C++多线程基本操作如下:
1. 线程的建立结束
2. 线程的互斥和同步
3. 使用信号量控制线程
4. 线程的基本属性配置
在C/C++代码编写时,使用多线程机制,首先需要做的事情就是声明引用,具体如下:
#include "pthread.h"
二、线程基本操作方法
基本线程操作:
1. pthread_create():创建线程开始运行相关线程函数,运行结束则线程退出
2. pthread_eixt():因为exit()是用来结束进程的,所以则需要使用特定结束线程的函数
3. pthread_join():挂起当前线程,用于阻塞式地等待线程结束,如果线程已结束则立即返回,0=成功
4. pthread_cancel():发送终止信号给thread线程,成功返回0,但是成功并不意味着thread会终止
5. pthread_testcancel():在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
6. pthread_setcancelstate():设置本线程对cancle线程的反应
7. pthread_setcanceltype():设置取消状态 继续运行至下一个取消点再退出或者是立即执行取消动作
8. pthread_setcancel():设置取消状态
三、线程互斥与同步机制
基本的互斥与同步的操作方法:
1. pthread_mutex_init():互斥锁的初始化
2. pthread_mutex_lock():锁定互斥锁,如果尝试锁定已经被上锁的互斥锁则阻塞至可用为止
3. pthread_mutex_trylock():非阻塞的锁定互斥锁
4. pthread_mutex_unlock():释放互斥锁
5. pthread_mutex_destory():互斥锁销毁函数
四、信号量线程控制机制
C/C++在使用信号量机制的时候,默认的信号量为匿名信号量。
1. sem_init(sem):初始化一个定位在sem的匿名信号量
2. sem_wait():把信号量减1操作,如果信号量的当前值为0则进入阻塞,为原子操作
3. sem_trywait():如果信号量的当前值为0则返回错误而不是阻塞调用(errno=EAGAIN),其实是sem_wait()的非阻塞版本
4. sem_post():给信号量的值加1,它是一个“原子操作”,即同时对同一个信号量做加1,操作的两个线程是不会冲突的
5. sem_getvalue(sval):把sem指向的信号量当前值放置在sval指向的整数上
6. sem_destory(sem):销毁由sem指向的匿名信号量
五、多线程实践
1. 基本的线程及建立运行
下面的代码是C/C++开发的基本的线程的运行,使用的就是最基本的pthread.h:
/* thread.c */ #include#include #include #define THREAD_NUMBER 3 /*线程数*/ #define REPEAT_NUMBER 5 /*每个线程中的小任务数*/ #define DELAY_TIME_LEVELS 10.0 /*小任务之间的最大时间间隔*/ // void *thrd_func(void *arg) { /* 线程函数例程 */ int thrd_num = (int)arg; int delay_time = 0; int count = 0; printf("Thread %d is starting\n", thrd_num); for (count = 0; count < REPEAT_NUMBER; count++) { delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1; sleep(delay_time); printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time); } printf("Thread %d finished\n", thrd_num); pthread_exit(NULL); } int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; srand(time(NULL)); for (no = 0; no < THREAD_NUMBER; no++) { /* 创建多线程 */ res = pthread_create(&thread[no], NULL, thrd_func, (void*)no); if (res != 0) { printf("Create thread %d failed\n", no); exit(res); } } printf("Create treads success\n Waiting for threads to finish...\n"); for (no = 0; no < THREAD_NUMBER; no++) { /* 等待线程结束 */ res = pthread_join(thread[no], &thrd_ret); if (!res) { printf("Thread %d joined\n", no); } else { printf("Thread %d join failed\n", no); } } return 0; }
例程中循环3次建立3条线程,并且使用pthread_join函数依次等待线程结束;
线程中使用rand()获取随机值随机休眠5次,随意会出现后执行的线程先执行完成;
运行结果:
$ gcc thread.c -lpthread $ ./a.out Create treads success Waiting for threads to finish... Thread 0 is starting Thread 1 is starting Thread 2 is starting Thread 1: job 0 delay = 2 Thread 1: job 1 delay = 2 Thread 0: job 0 delay = 8 Thread 2: job 0 delay = 10 Thread 2: job 1 delay = 3 Thread 1: job 2 delay = 10 Thread 0: job 1 delay = 8 Thread 0: job 2 delay = 3 Thread 0: job 3 delay = 1 Thread 2: job 2 delay = 8 Thread 1: job 3 delay = 8 Thread 1: job 4 delay = 1 Thread 1 finished Thread 2: job 3 delay = 6 Thread 0: job 4 delay = 7 Thread 0 finished Thread 0 joined Thread 1 joined Thread 2: job 4 delay = 10 Thread 2 finished Thread 2 joined
可以看到,线程1先于线程0执行,但是pthread_join的调用时间顺序,先等待线程0执行;
由于线程1已经早结束,所以线程0被pthread_join等到的时候,线程1已结束,就在等待到线程1时,直接返回;
2. 线程执行的互斥和同步pthread_mutex_lock
下面我们在上面的程序中增加互斥锁:
/*thread_mutex.c*/ #include#include #include #define THREAD_NUMBER 3 /* 线程数 */ #define REPEAT_NUMBER 3 /* 每个线程的小任务数 */ #define DELAY_TIME_LEVELS 10.0 /*小任务之间的最大时间间隔*/ pthread_mutex_t mutex; void *thrd_func(void *arg) { int thrd_num = (int)arg; int delay_time = 0, count = 0; int res; /* 互斥锁上锁 */ res = pthread_mutex_lock(&mutex); if (res) { printf("Thread %d lock failed\n", thrd_num); pthread_exit(NULL); } printf("Thread %d is starting\n", thrd_num); for (count = 0; count < REPEAT_NUMBER; count++) { delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1; sleep(delay_time); printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time); } printf("Thread %d finished\n", thrd_num); pthread_exit(NULL); } int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; srand(time(NULL)); /* 互斥锁初始化 */ pthread_mutex_init(&mutex, NULL); for (no = 0; no < THREAD_NUMBER; no++) { res = pthread_create(&thread[no], NULL, thrd_func, (void*)no); if (res != 0) { printf("Create thread %d failed\n", no); exit(res); } } printf("Create treads success\n Waiting for threads to finish...\n"); for (no = 0; no < THREAD_NUMBER; no++) { res = pthread_join(thread[no], &thrd_ret); if (!res) { printf("Thread %d joined\n", no); } else { printf("Thread %d join failed\n", no); } } /****互斥锁解锁***/ pthread_mutex_unlock(&mutex); pthread_mutex_destroy(&mutex); return 0; }
在上面的例程中直接添加同步锁pthread_mutex_t;
在线程中加入,于是程序在执行线程程序时;
调用pthread_mutex_lock上锁,发现上锁时候后进入等待,等待锁再次释放后重新上锁;
所以线程程序加载到队列中等待,等待成功上锁后继续执行程序代码;
运行结果如下:
$gcc thread_mutex.c -lpthread $ ./a.out Create treads success Waiting for threads to finish... Thread 0 is starting Thread 0: job 0 delay = 9 Thread 0: job 1 delay = 4 Thread 0: job 2 delay = 7 Thread 0 finished Thread 0 joined Thread 1 is starting Thread 1: job 0 delay = 6 Thread 1: job 1 delay = 4 Thread 1: job 2 delay = 7 Thread 1 finished Thread 1 joined Thread 2 is starting Thread 2: job 0 delay = 3 Thread 2: job 1 delay = 1 Thread 2: job 2 delay = 6 Thread 2 finished Thread 2 joined
3. 使用信号量控制线程的执行顺序sem_post
修改上面例程,上面的是使用pthread_mutex_lock互斥锁控制线程执行顺序,
使用另外一种线程执行顺序的控制:
/* thread_sem.c */ #include#include #include #include #define THREAD_NUMBER 3 #define REPEAT_NUMBER 3 #define DELAY_TIME_LEVELS 10.0 sem_t sem[THREAD_NUMBER]; void * thrd_func(void *arg) { int thrd_num = (int)arg; int delay_time = 0; int count = 0; sem_wait(&sem[thrd_num]); printf("Thread %d is starting\n", thrd_num); for (count = 0; count < REPEAT_NUMBER; count++) { delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1; sleep(delay_time); printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time); } printf("Thread %d finished\n", thrd_num); pthread_exit(NULL); } int main(void) { pthread_t thread[THREAD_NUMBER]; int no = 0, res; void * thrd_ret; srand(time(NULL)); for (no = 0; no < THREAD_NUMBER; no++) { sem_init(&sem[no], 0, 0); res = pthread_create(&thread[no], NULL, thrd_func, (void*)no); if (res != 0) { printf("Create thread %d failed\n", no); exit(res); } } printf("Create treads success\n Waiting for threads to finish...\n"); sem_post(&sem[THREAD_NUMBER - 1]); for (no = THREAD_NUMBER - 1; no >= 0; no--) { res = pthread_join(thread[no], &thrd_ret); if (!res) { printf("Thread %d joined\n", no); } else { printf("Thread %d join failed\n", no); } sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]); } for (no = 0; no < THREAD_NUMBER; no++) { sem_destroy(&sem[no]); } return 0; }
执行结果,仍然是建立3条线程,每条线程执行时休眠随机时长:
$ gcc thread_sem.c -lpthread $ ./a.out Create treads success Waiting for threads to finish... Thread 2 is starting Thread 2: job 0 delay = 9 Thread 2: job 1 delay = 9 Thread 2: job 2 delay = 5 Thread 2 finished Thread 2 joined Thread 1 is starting Thread 1: job 0 delay = 5 Thread 1: job 1 delay = 7 Thread 1: job 2 delay = 4 Thread 1 finished Thread 1 joined Thread 0 is starting Thread 0: job 0 delay = 3 Thread 0: job 1 delay = 9 Thread 0: job 2 delay = 8 Thread 0 finished Thread 0 joined
执行结果与第2个例程非常相似,只不过教材中进行倒序执行而已;
那么这种方式其实与使用互斥锁相比,代码量可读性基本持平不相上下;