多线程编程-互斥锁条件变量关卡的使用实例

xiaoxiao2021-02-28  132

序言

之前的文章已经回顾了多线程同步的6种方式,这里就互斥锁和条件变量的配合使用,以及关卡的使用举个栗子。

以牛客网上迅雷的一道面试题为例。

1. 题目

编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C。 每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。

2. 实现思路

[1] 不能只使用互斥锁,因为互斥锁的特点是资源的”无序访问“

[2] 可以使用信号量,三个线程之间形成依次通知的关系,由主线程通知启动。

[3] 使用条件变量,三个线程之间形成相互等待的关系,可用条件变量通知等待的线程,由主线程通知启动。

这里使用条件变量进行实现。

3. 一些线程函数原型

线程创建函数:int pthread_create (pthread_t *tid, const pthread_attr_t * attr, void * (void *), void *arg);

等待线程结束函数:int pthread_join(pthread_t tid, void **ret);

互斥锁初始化函数:int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

互斥锁上锁:int pthread_mutex_lock (pthread_mutex_t *mutex);

条件变量初始化函数: int pthread_cond_init(pthread_cond_t *condition, pthread_condattr_t *condattr);

条件变量等待函数:int pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *mutex);

条件变量定时等待函数:int pthread_cond_timedwait(pthread_cond_t *condition, pthread_mutex_t *mutex, const struct timespec *abstime);

条件变量通知函数:int pthread_cond_signal(pthread_cond_t *cond);

条件变量广播函数:int pthread_cond_broadcast(pthread_cond_t *cond);

关卡初始化函数:int pthread_barrier_init( pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count);

关卡等待函数:int pthread_barrier_wait( pthread_barrier_t *barrier);

4. 代码实现 - 使用pthread_join()

#include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_t A, B, C; //线程ID pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //互斥锁,条件变量对资源的互斥访问 pthread_cond_t condition = PTHREAD_COND_INITIALIZER; //条件变量 int flag = 0; //把线程阻塞在条件变量的条件,全局变量 void *printThread1(void *arg); void *printThread2(void *arg); void *printThread3(void *arg); int main() { /*动态初始化方式 pthread_mutex_init(&lock, NULL); pthread_cond_init(&condition, NULL); */ //建立三个子线程 int res; if ((res = pthread_create(&A, NULL, printThread1, NULL)) != 0) { printf("线程A创建失败\n"); exit(-1); } //条件置位让线程1开始执行 flag = 1; if ((res = pthread_create(&B, NULL, printThread2, NULL)) != 0) { printf("线程B创建失败\n"); exit(-1); } if ((res = pthread_create(&C, NULL, printThread3, NULL)) != 0) { printf("线程C创建失败\n"); exit(-1); } //等待各个子线程结束 pthread_join(A, NULL); pthread_join(B, NULL); pthread_join(C, NULL); //释放内存之前销毁互斥锁和条件变量 pthread_mutex_destroy(&lock); pthread_cond_destroy(&condition); return 0; } void *printThread1(void *arg) { int i = 0; while (i < 10) { pthread_mutex_lock(&lock); if (flag != 1) //flag == 1时,线程1执行 pthread_cond_wait(&condition, &lock); printf("A"); flag = 2; i++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } return NULL; //或return ((void *)0); } void *printThread2(void *arg) { int j = 0; while (j < 10) { pthread_mutex_lock(&lock); if (flag != 2) //flag == 2时,线程2执行 pthread_cond_wait(&condition, &lock); printf("B"); flag = 3; j++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } return NULL; } void *printThread3(void *arg) { int k = 0; while (k < 10) { pthread_mutex_lock(&lock); if (flag != 3) //flag == 3时,线程3执行 pthread_cond_wait(&condition, &lock); printf("C"); flag = 1; k++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } return NULL; }

说明:

[1] 互斥锁和条件变量静态创建,因此可以选择静态初始化(赋值)或者动态初始化(函数)的方式

[2] 之前将条件置位语句flag = 1放在了创建完线程之后,发现有的时候能正常运行输出,有的时候又完全没有反应。问题在于线程创建之后,运行哪一个是随机的,如果不是线程1条件在等待,则不会继续运行。

[3] pthread_join()对不同函数的等待没有先后顺序,所以pthread_join(A, NULL),pthread_join(B, NULL),pthread_join(C, NULL)谁在前谁在后都没有关系。

[4] int i, j, k的定义放到了各个线程中而不是作为全局变量,因为线程运行起来后直至运行结束才会退出,因此可作为局部变量。

4. 代码实现 - 使用关卡Barriers

#include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_t A, B, C; //线程ID pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //互斥锁,条件变量对资源的互斥访问 pthread_cond_t condition = PTHREAD_COND_INITIALIZER; //条件变量 pthread_barrier_t barrier; //关卡 int flag = 0; //把线程阻塞在条件变量的条件,全局变量 void *printThread1(void *arg); void *printThread2(void *arg); void *printThread3(void *arg); int main() { //关卡初始化,count = 子线程个数 + 1(主线程) pthread_barrier_init(&barrier, NULL, 4); //建立三个子线程 int res; if ((res = pthread_create(&A, NULL, printThread1, NULL)) != 0) { printf("线程A创建失败\n"); exit(-1); } //条件置位让线程1开始执行 flag = 1; if ((res = pthread_create(&B, NULL, printThread2, NULL)) != 0) { printf("线程B创建失败\n"); exit(-1); } if ((res = pthread_create(&C, NULL, printThread3, NULL)) != 0) { printf("线程C创建失败\n"); exit(-1); } //设置线程关卡,每个线程运行到关卡均等待 pthread_barrier_wait(&barrier); //释放内存之前销毁互斥锁和条件变量 pthread_mutex_destroy(&lock); pthread_cond_destroy(&condition); pthread_barrier_destroy(&barrier); return 0; } void *printThread1(void *arg) { int i = 0; while (i < 10) { pthread_mutex_lock(&lock); if (flag != 1) pthread_cond_wait(&condition, &lock); printf("A"); flag = 2; i++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } pthread_barrier_wait(&barrier); return NULL; } void *printThread2(void *arg) { int j = 0; while (j < 10) { pthread_mutex_lock(&lock); if (flag != 2) pthread_cond_wait(&condition, &lock); printf("B"); flag = 3; j++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } pthread_barrier_wait(&barrier); return NULL; } void *printThread3(void *arg) { int k = 0; while (k < 10) { pthread_mutex_lock(&lock); if (flag != 3) pthread_cond_wait(&condition, &lock); printf("C"); flag = 1; k++; pthread_cond_signal(&condition); pthread_mutex_unlock(&lock); } pthread_barrier_wait(&barrier); return NULL; }

pthread_cond_wait()函数说明:

[1] 抢占到互斥锁并运行到条件if (flag != 1)之后,线程阻塞在条件变量

[2] pthread_cond_wait()内部会解锁,然后等待条件变量被其他线程激活

[3] 被激活后会再自动加锁(在其他线程释放互斥锁之后)

[4] 并重新判断条件变量阻塞条件是否成立,成立重新挂起,不成立则继续往下执行

Acknowledgements: http://blog.csdn.net/ithomer/article/details/6031723 http://blog.csdn.net/freedom8531/article/details/45419149

2017.08.31

转载请注明原文地址: https://www.6miu.com/read-20450.html

最新回复(0)