序言
之前的文章已经回顾了多线程同步的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;
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()
{
int res;
if ((res = pthread_create(&A,
NULL, printThread1,
NULL)) !=
0)
{
printf(
"线程A创建失败\n");
exit(-
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)
pthread_cond_wait(&condition, &lock);
printf(
"A");
flag =
2;
i++;
pthread_cond_signal(&condition);
pthread_mutex_unlock(&lock);
}
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);
}
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);
}
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;
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()
{
pthread_barrier_init(&barrier, NULL,
4);
int res;
if ((res = pthread_create(&A, NULL, printThread1, NULL)) !=
0)
{
printf(
"线程A创建失败\n");
exit(-
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