linux中多线程c++编程同步对象的使用
对于多线程程序来说,同步是指在同一时刻只有有个线程能够访问某一公共资源;在linux中可以使用互斥锁(mutex)、条件变量(condtion varirable)、读写锁(reader-writer-lock)和信号量(semphore)来实现资源同步。
1、mutex
mutex简介及使用:
mutex的初始化包括静态和动态初始化两种,静态初始化使用 PTHREAD_MUTEX_INITIALIZER
常量即可初始化,动态初始化使用 pthread_mutex_ini函数初始化,使用完之后需要使用
pthread_mutex_destory释放;使用 pthread_mutex_lock 对需要同步的资源加锁,使用
pthread_mutex_unlock 释放锁。
例:
//静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//动态初始化
pthread_mutex_t mutex;
pthread_mutex_ini(&mutex, NULL);
//加锁
pthread_mutex_lock(&mutex);
…
//解锁
pthread_mutex_unlock(&mutex);
/*
* 模拟4个窗口出售20张票,如果不加锁;可能就会出现售出的票比实际的票多的情况
*/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int total_ticket_num = 20;
pthread_mutex_t mutex_x = PTHREAD_MUTEX_INITIALIZER;
void *sell_ticket(void*)
{
for (int i = 0; i < 20; i++)
{
pthread_mutex_lock(&mutex_x);
if (total_ticket_num > 0)
{
sleep(1);
printf("sell the %dth ticket\n", 20 - total_ticket_num + 1);
total_ticket_num--;
}
pthread_mutex_unlock(&mutex_x);
}
return 0;
}
int main()
{
pthread_t tids[4];
int iret;
int i;
for (i = 0; i < 4; i++)
{
iret = pthread_create(&tids[i], NULL, sell_ticket, NULL);
if (iret)
{
printf("pthread_create error : %d\n", iret);
return iret;
}
}
sleep(20);
void *retval;
for (i = 0; i < 4; i++)
{
iret = pthread_join(tids[i], &retval);
if (iret)
{
printf("pthrad_join error : %d\n", iret);
return iret;
}
printf("the thread : %ld state is : %ld\n", tids[i], (long*)retval);
}
return 0;
}
2、condtion variable
condtion variable的初始化和销毁;
//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//动态初始化
pthread_cond_t cond;
pthread_cond_ini(&cond, NULL);
//销毁
pthread_cond_destory(&cond);
condtion variable其他相关函数简介和使用;条件变量的使用需要和互斥锁相互配合
例:
pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
//在使用 pthread_cond_wait 函数使线程进入等待状态前,需先对公共资源加锁
pthread_mutex_lock(&pmutex);
…
pthread_cond_wait(&pcond, &pmutex);
…
pthread_mutex_unlock(&pmutex);
pthread_cond_wait 之后的代码不再执行,当接收到来自 pthread_cond_signal 的信号时
将继续执行,如果有多个线程都处于 pthread_cond_wait ; pthrea_cond_signal 只会唤醒
其中的一个线程;唤醒顺序规则:线程优先级高的先被唤醒,优先级相同的,等待时间
长的先被唤醒
/*
* 条件变量关于出租车应用举例
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//提示处注册到达的条件变量
pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
//同步锁
pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
void *traveler_arrive(void *args)
{
printf("Traveler %s needs a taxi now!\n", (char*)args);
pthread_mutex_lock(&pmutex);
pthread_cond_wait(&pcond, &pmutex);
pthread_mutex_unlock(&pmutex);
printf("Traveler %s now got a taxi!\n", (char*)args);
pthread_exit((void*)0);
}
void *taxi_arrive(void *args)
{
printf("%s's taxi arrived!\n", (char*)args);
pthread_cond_signal(&pcond);
pthread_exit((void*)0);
}
int main()
{
pthread_t tids[3];
int iret;
iret = pthread_create(&tids[0], NULL, taxi_arrive, (void*)("Jack"));
if (iret)
{
printf("pthread_create error : %d\n", iret);
return iret;
}
printf("Time passing By.\n");
sleep(1);
iret = pthread_create(&tids[1], NULL, traveler_arrive, (void*)("SuSan"));
if (iret)
{
printf("pthread_create error : %d\n", iret);
return iret;
}
printf("Time Passing By.\n");
sleep(1);
iret = pthread_create(&tids[2], NULL, taxi_arrive, (void*)("Mike"));
if (iret)
{
printf("pthread_create error : %d\n", iret);
return iret;
}
printf("Time Passing By.\n");
sleep(1);
void *retval;
for (int i = 0; i < 3; i++)
{
iret = pthread_join(tids[i], &retval);
if (iret)
{
printf("pthread_join error : %d\n", iret);
return iret;
}
printf("The thread : %lu exit code is : %ld\n", tids[i], *(long*)retval);
}
return 0;
}
/*
* 程序分析:
* 实例共有2个线程函数:旅客、出租车分别用来表示旅客到达和出租车到达;
* 首先创建一个出租车的线程表示Jack的出租车已经到达,处于等乘客的状态;
* 在创建一个旅客线程SuSan,SuSan到达后需要乘坐出租车;最后在创建一个
* 出租车的线程Mike。最总结果,SuSan乘坐出租车离开;但是我们并不知道,
* SuSan是乘坐的谁的出租车离开的,还有就是SuSan到的时候Jack的出租车已经
* 在等待了但是SuSan为什么不坐。其实造成这两个问题的根本原因是,驾驶
* 出租车的驾驶员只会在出租车到的时候喊一嗓子(“×××驾驶的出租车到了,
* 有没有需要乘车的,之后就再也不会主动去招揽乘客了,出租车驾驶员可能
* 比较懒不愿意多说话”),还有一个原因是乘客都是不会说话呢的盲人,只能
* 听见。所以要解决让Jack、SuSan不要白等的问题就需要,就需要让出租车驾
* 驶员变得勤快一点(无论有没有乘客需要乘车,都要一直吆喝有没有乘客需要
* 乘车,直到有乘客乘坐他的出租车为止)。下面是一个勤快的出租车驾驶员
*/
int traveler_count = 0;
void *advance_traveler_arrire(void *args)
{
printf("Traveler %s needs a taxi now!\n", (char*)args);
pthread_mutex_lock(&pmutex);
++traveler_count;
pthread_cond_wait(&pcond, &pmutex);
pthread_mutex_unlock(&pmutex);
printf("Traveler %s now got a taxi!\n", (char*)args);
pthread_exit((void*)0);
pthread_exit((void*)0);
}
void *advance_taxi_arrire(void *args)
{
printf("%s's taxi arrived!\n", (char*)args);
while (1)
{
if (traveler_count > 0)
{
pthread_mutex_lock(&pmutex);
--traveler_count;
pthread_mutex_unlock(&pmutex);
pthread_cond_signal(&pcond);
}
}
pthread_exit((void*)0);
}
/*
* 其实经过改写后的程序就模拟了生产者和消费者
*/
3、reader-writer-lock
读写锁的特性和应用场景:
(1)读写锁比起互斥锁具有更高的实用性与并行性,可以有多个线程同时占用读模式
的读写锁,但是只能有一个想成占用写模式的读写锁,读写搜的3中状态如下所述。
1)当读写时写加锁状态时,这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻
塞。
2)当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都是可以得到访问
权限的,但是以写模式对它进行加锁的线程将会被阻塞。
3)当读写锁在读模式的所状态时,如果有另外的线程试图以写模式加锁,读写锁通常会
阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁的请求
则长期阻塞。
处理读者–写者问题的两种常见策略是强读者同步和强写者同步。在强读者同步中,总是
给读者更高的优先权,只要写者当前没有进行写操作,读者就可以获得访问权限;而在强
写者同步中,则往往将优先权交付給写者,而读者只能等待所有正在等待的或者正在执行
的写者结束以后才能执行。关于读者–写者模型,由于读者往往要求查看最新的信息记录,
例如航班订票系统往往会使用强写者同步策略,而图书馆查阅系统则采用前读者同步策略。
(2)读写锁机制是由POSIX提供的,如果写者没有持有写锁,那么所有的读者都可以持有
这把锁,而一旦有某个写者阻塞在上锁的时候,那么就由POSIX系统决定是否允许读者获取
该锁。
读写锁相关的函数使用,如下所述。
1)初始化和销毁读写锁
//静态分配
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
//动态分配
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
//销毁
pthread_rwlock_destory(&rwlock);
//获取读出锁,如果相应的读出锁已经被写者占用,那么就阻塞调用线程
pthread_rwlock_rdlock(&rwlock);
//获取一个写者锁,如果相应的写入锁已经被其他的写者或读者占用,那么就阻塞该线程
pthread_rwlock_wrlock(&rwlock);
//释放一个读者或写者锁
pthread_rwlock_unlock(&rwlock);
注:其中读写加锁的函数的另外两个非阻塞函数如下:
pthread_rwlock_tryrdlock(&rwlock);
pthread_rwlock_trywrlock(&rwlock);
这两个函数获得锁返回0,获取锁失败返回EBUSY
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_rwlock_t rwlock;
#define THREADNUM 5
void *readers(void *args)
{
pthread_rwlock_rdlock(&rwlock);
printf("reader %ld got the lock!\n", (long)args);
pthread_rwlock_unlock(&rwlock);
pthread_exit((void*)0);
}
void *writers(void *args)
{
pthread_rwlock_wrlock(&rwlock);
printf("writer %ld got the lock!\n", (long)args);
pthread_rwlock_unlock(&rwlock);
pthread_exit((void*)0);
}
int main()
{
int iret, i;
pthread_t writer_id, reader_id;
pthread_attr_t attr;
int nreadercount = 1, nwritercount = 1;
iret = pthread_rwlock_init(&rwlock, NULL);
if (iret)
{
fprintf(stderr, "init lock failed!\n");
return iret;
}
//初始化线程属性
pthread_attr_init(&attr);
//设置线程的分离状态,即调用 pthread_join 不能获取线程的状态码
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
for (i = 0; i < THREADNUM; i++)
{
if (i % 3)
{
iret = pthread_create(&reader_id, &attr, readers, (void*)nreadercount);
if (iret == 0)
{
printf("create reader thread successed, the thread id is : %lu\n", (unsigned long)reader_id);
++nreadercount;
}
}
else
{
iret = pthread_create(&writer_id, &attr, writers, (void*)nwritercount);
if (iret == 0)
{
printf("create writer thread successed, the thread is is : %lu\n", (unsigned long)writer_id);
++nwritercount;
}
}
}
sleep(5);
return 0;
}