昨天笔试唯品会时,遇到一道编程题:
编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。’
当时看到这道题时,就想到了要使用互斥锁和条件变量。奈何,没有深入了解过,只能干瞪眼。回来之后,在网上查了查资料,也找到了相关题目,故整理这篇文章,算是学习笔记吧。
代码直接借用别人的,就不亲自写了。代码原出处为:http://blog.chinaunix.net/uid-26868581-id-3359815.html
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define DEBUG 1
int num = 0;
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t qready=PTHREAD_COND_INITIALIZER;
void* thread_func(void *arg)
{
int param = (int) arg;
int i;
for(i = 0; i < 10; i++)
{
pthread_mutex_lock(&mylock);
while(param != num)
pthread_cond_wait(&qready, &mylock);
printf("%c", param + 'A');
num = (num + 1) % 3;
pthread_mutex_unlock(&mylock);
pthread_cond_broadcast(&qready);
}
return (void *)0;
}
int main()
{
int i;
pthread_t tid[3];
void *tret;
for(i = 0; i < 3; i++)
pthread_create(&tid[i], NULL, thread_func, (void *) i);
for(i = 0; i < 3; i++)
pthread_join(tid[i], &tret);
}
程序思想比较简单,定义一个互斥锁和条件变量,开辟三个线程。在线程中,首先对互斥锁上锁,当数字与当前进程号相同时,执行打印,否则条件变量等待中(注,进入条件等待后,会对互斥锁解锁。当收到信号,激活时,重新对互斥锁上锁)。
程序中主要涉及到的知识点如下:
1.互斥锁与条件变量的初始化
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t qready=PTHREAD_COND_INITIALIZER;
2.条件变量的等待方式
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
等待条件有两种方式:条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
3.激发条件的两种方式
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
4.线程结束thread_join
pthread_join使一个线程等待另一个线程结束。代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。