引言
分享关于线程的一道测试题,因为网上基本都是Java的解决方法,决定自己写一篇来记录一下线程的学习。
问题描述
编写一个至少具有三个线程的程序(称之为线程 A、B 和 C),其中线程 A 输出字符’A’,线程 B 输出字符’B’,线程 C 输出字符’C’。使得最终输出结果为“ABCABCABC…”。
思路
拿到这题后,我的想法是把问题简化。
原题让我们交替打印ABC,那么我们只要先考虑如何交替打印AB即可,然后在B之后衔接C,在C之后衔接A即可。
那么,如何使两个线程能够按照顺序去执行任务呢?
关于线程同步,首先我们想到的是互斥锁mutex。
但是mutex的缺点很明显,只有简单的拿锁和解锁两个状态,并且你无法保证谁先拿锁,也就是说你没法保证两个线程按照顺序去执行任务。
在Linux中,为了配合互斥锁mutex的使用,引入了条件变量cond。
它允许一个线程阻塞,直到另一个线程唤醒它。因此,我们可以想到用cond来实现顺序,解出题目。
具体的思路是:
设置三个不同的条件变量,分别代表前置线程是否结束任务时间(这样才能一环套一环才能保证线程按照顺序打印呀);配套这些条件变量的是三个互斥锁,分别代表哪个线程处于任务时间。
例如说,负责打印A的线程A的先等待标志线程C已经结束任务的条件变量唤醒,我们命名为c_over,来表示线程C已经结束任务。线程C结束任务后,轮到线程A工作了,此时线程A拿锁,同样的我们把这把锁命名为a_time,表示是线程A的工作时间,此时线程B和线程C都不会工作。最后,线程A打印出字符A后,释放这把a_time,表示线程A结束任务,同时通过a_over唤醒线程B。
注意点
一、
如果所有线程都像上述这么做,也就是所有线程都等待条件变量唤醒而阻塞着,没法工作。
解决方案:
在主线程中唤醒线程A
二、
若在主线程中直接使用return 0退出,内核会回收掉整个进程的资源,结束其余线程的任务。
解决方案:
在主线程中调用pthread_exit()或是在最后加上一行while (1);
代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
pthread_mutex_t a_time, b_time, c_time;
pthread_cond_t a_over = PTHREAD_COND_INITIALIZER;
pthread_cond_t b_over = PTHREAD_COND_INITIALIZER;
pthread_cond_t c_over = PTHREAD_COND_INITIALIZER;
void my_err(const char *str)
{
perror(str);
exit(1);
}
void *func_a(void *arg)
{
while (1)
{
pthread_mutex_lock(&a_time);
pthread_cond_wait(&c_over, &a_time);
putchar('A');
pthread_mutex_unlock(&a_time);
pthread_cond_signal(&a_over);
}
}
void *func_b(void *arg)
{
while (1)
{
pthread_mutex_lock(&b_time);
pthread_cond_wait(&a_over, &b_time);
putchar('B');
pthread_mutex_unlock(&b_time);
pthread_cond_signal(&b_over);
}
}
void *func_c(void *arg)
{
while (1)
{
pthread_mutex_lock(&c_time);
pthread_cond_wait(&b_over, &c_time);
putchar('C');
pthread_mutex_unlock(&c_time);
pthread_cond_signal(&c_over);
}
}
int main(int argc, char *argv[])
{
pthread_t tid_a, tid_b, tid_c;
pthread_mutex_init(&a_time, NULL);
pthread_mutex_init(&b_time, NULL);
pthread_mutex_init(&c_time, NULL);
pthread_create(&tid_a, NULL, func_a, NULL);
pthread_create(&tid_b, NULL, func_b, NULL);
pthread_create(&tid_c, NULL, func_c, NULL);
pthread_cond_signal(&c_over);
pthread_exit( (void*)0 ); //while (1);
}