linux 线程的同步 二 (互斥锁和条件变量)
互斥锁和条件变量
为了允许在线程或进程之间共享数据,同步时必须的,互斥锁和条件变量是同步的基本组成部分。
1、互斥锁
互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥锁通常用于保护由多个线程或多进程分享的共享数据。一般是一些可供线程间使用的全局变量,来达到线程同步的目的,即保证任何时刻只有一个线程或进程在执行其中的代码。一般加锁的轮廓如下:
pthread_mutex_lock()
临界区
pthread_mutex_unlock()
互斥锁API
pthread_mutex_lock(pthread_mutex_t *mutex);
用此函数加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。当此函数返回时,说明互斥锁已经被当前线程成功加锁.
pthread_mutex_trylock(pthread_mutex_t *mutex);
用此函数加锁时,如果mutex已经卑琐主,当前尝试加锁的线程不会阻塞,而是立即返回,返回的错误码为EBUSY,而不是阻塞等待。
pthread_mutex_unlock(pthread_mutex_t *mutex);
注意使用锁之前要记得初始化。互斥锁的初始化有两种初始化方式:
1.对于静态分配的互斥锁一半用宏赋值的方式初始化
eg: static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2.对于动态分配的互斥锁(如调用malloc)或分配在共享内存中,则必须调用pthread_mutex_init(pthread_mutex *mutex, pthread_mutexattr_t *mutexattr)函数来进行初始化。
例子1:写个程序实现生产者—消费者问题,先只考虑多个生产者线程之间的同步,直到所有的生产者线程都完成工作以后,才启动消费者线程。程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h> #define MAXNITEMS 1000000
#define MAXNTHREADS 100 int nitems; struct
{
pthread_mutex_t mutex;
int buff[MAXNITEMS];
int nput;
int nval;
} shared = {
PTHREAD_MUTEX_INITIALIZER
}; void *produce(void*);
void *consume(void*); int main(int argc,char *argv[])
{
int i,nthreads,count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS],tid_consume;
if(argc != )
{
printf("usage: producongs2 <#itmes> <#threads>.\n");
exit();
}
nitems = atoi(argv[]);
nthreads = atoi(argv[]);
pthread_setconcurrency(nthreads); //设置线程并发级别
for(i=;i<nthreads;++i)
{
count[i] = ;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
}
for(i=;i<nthreads;i++)
{
pthread_join(tid_produce[i],NULL); //等待线程退出
printf("count[%d] = %d\n",i,count[i]);
}
pthread_create(&tid_consume,NULL,consume,NULL);
pthread_join(tid_consume,NULL); //等待线程退出
exit();
} void *produce(void *arg)
{
for(; ;)
{
pthread_mutex_lock(&shared.mutex); //加锁
if(shared.nput >= nitems)
{
pthread_mutex_unlock(&shared.mutex); //释放锁
return ;
}
shared.buff[shared.nput] = shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex); //加锁
*((int*) arg) += ;
}
}
void *consume(void *arg)
{
int i;
for(i=;i<nitems;i++)
{
if(shared.buff[i] != i)
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
return;
}
程序执行结果如下:
例子2:改进例子1,所有生产者线程启动后立即启动消费者线程,这样生产者线程产生数据的同时,消费者线程就能出来它,此时必须同步生产者和消费者,程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h> #define MAXNITEMS 1000000
#define MAXNTHREADS 100 int nitems; struct
{
pthread_mutex_t mutex;
int buff[MAXNITEMS];
int nput;
int nval;
} shared = {
PTHREAD_MUTEX_INITIALIZER
}; void *produce(void*);
void *consume(void*);
void consume_wait(int);
int main(int argc,char *argv[])
{
int i,nthreads,count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS],tid_consume;
if(argc != )
{
printf("usage: producongs2 <#itmes> <#threads>.\n");
exit();
}
nitems = atoi(argv[]);
nthreads = atoi(argv[]);
pthread_setconcurrency(nthreads+);
//创建生产者线程
for(i=;i<nthreads;++i)
{
count[i] = ;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
}
//创建消费者线程
pthread_create(&tid_consume,NULL,consume,NULL);
for(i=;i<nthreads;i++)
{
pthread_join(tid_produce[i],NULL);
printf("count[%d] = %d\n",i,count[i]);
}
//等待消费者线程退出
pthread_join(tid_consume,NULL);
exit();
} void *produce(void *arg)
{
for(; ;)
{
pthread_mutex_lock(&shared.mutex);
if(shared.nput >= nitems)
{
pthread_mutex_unlock(&shared.mutex);
return ;
}
shared.buff[shared.nput] = shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex);
*((int*) arg) += ;
}
}
void *consume(void *arg)
{
int i;
for(i=;i<nitems;i++)
{
consume_wait(i);
if(shared.buff[i] != i)
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
return;
}
void consume_wait(int i)
{
for(; ;) //进行轮询,判断i是否已经由生产者生产
{
pthread_mutex_lock(&shared.mutex);
if(i<shared.nput) //i已经生产
{
pthread_mutex_unlock(&shared.mutex);
return;
}
pthread_mutex_unlock(&shared.mutex);
}
}
存在的问题:当消费者获取的条目尚没有准备好时,消费者线程一次次的循环去判断,每次给互斥锁解锁又上锁,这种轮询的办法浪费CPU时间。
2、条件变量
互斥锁用于上锁,条件变量用于等待,条件变量的使用是与互斥锁共通使用的。
2.1等待与信号发送
条件变量类型是pthread_cond_t,调用函数如下:
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *pmutex);
pthread_cond_signal(pthread_cond_t *pcond);
每个条件变量总是有一个互斥锁与之关联。现在采用条件变量实现生产者与消费者问题,程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h> #define MAXNITEMS 1000000
#define MAXNTHREADS 100 int nitems; struct
{
pthread_mutex_t mutex;
int buff[MAXNITEMS];
int nput;
int nval;
} shared = {
PTHREAD_MUTEX_INITIALIZER
};
//条件变量
struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int nready;
}nready = {
PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER
}; void *produce(void*);
void *consume(void*); int main(int argc,char *argv[])
{
int i,nthreads,count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS],tid_consume;
if(argc != )
{
printf("usage: producongs2 <#itmes> <#threads>.\n");
exit();
}
nitems = atoi(argv[]);
nthreads = atoi(argv[]);
pthread_setconcurrency(nthreads+);
for(i=;i<nthreads;++i)
{
count[i] = ;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
}
pthread_create(&tid_consume,NULL,consume,NULL);
for(i=;i<nthreads;i++)
{
pthread_join(tid_produce[i],NULL);
printf("count[%d] = %d\n",i,count[i]);
}
pthread_join(tid_consume,NULL);
exit();
} void *produce(void *arg)
{
printf("producer begins work\n");
for(; ;)
{
pthread_mutex_lock(&shared.mutex);
if(shared.nput >= nitems)
{
pthread_mutex_unlock(&shared.mutex);
return ;
}
shared.buff[shared.nput] = shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex);
pthread_mutex_lock(&nready.mutex);
if(nready.nready == )
pthread_cond_signal(&nready.cond); //通知消费者
nready.nready++;
pthread_mutex_unlock(&nready.mutex);
*((int*) arg) += ;
}
}
void *consume(void *arg)
{
int i;
printf("consuemer begins work.\n");
for(i=;i<nitems;i++)
{
pthread_mutex_lock(&nready.mutex);
while(nready.nready == )
pthread_cond_wait(&nready.cond,&nready.mutex); //等待生产者
nready.nready--;
pthread_mutex_unlock(&nready.mutex);
if(shared.buff[i] != i)
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
return;
}
程序执行结果如下:
总的来说,给条件变量发送信号的过程代码如下:
struct
{
pthread_mutex_t mutex;
pthread_cond_t cond;
//维护本条件的各个变量
}var = {PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER,...} pthread_mutex_lock(&var.mutex);
设置条件为真
pthread_cond_signal(&var.cond);
pthread_mutex_unlock(&var.mutex);
测试条件并进入睡眠以等待条件变为真的代码大体如下:
pthread_mutex_lock(&var.mutex);
while(条件为假)
pthread_cond_wait(&var.cond,&var.mutex);
修改条件
pthread_mutex_unlock(&var.mutex);
2.2定时等待和广播
通常pthread_cond_signal只是唤醒等待在相应条件变量上的一个线程,在某些情况下需要唤醒多个线程(例如读写者问题),可以调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。pthread_cond_timewait允许线程就阻塞时间设置一个限制值。API如下:
pthread_cond_broadcast(pthread_cond_t *cond);
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex, const struct timespec *abstime);
linux 线程的同步 二 (互斥锁和条件变量)的更多相关文章
- 【Linux C 多线程编程】互斥锁与条件变量
一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1) 初始化: 在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化: 对于静态 ...
- Linux互斥锁、条件变量和信号量
Linux互斥锁.条件变量和信号量 来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...
- linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁
Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图: 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...
- 非常精简的Linux线程池实现(一)——使用互斥锁和条件变量
线程池的含义跟它的名字一样,就是一个由许多线程组成的池子. 有了线程池,在程序中使用多线程变得简单.我们不用再自己去操心线程的创建.撤销.管理问题,有什么要消耗大量CPU时间的任务通通直接扔到线程池里 ...
- 线程私有数据TSD——一键多值技术,线程同步中的互斥锁和条件变量
一:线程私有数据: 线程是轻量级进程,进程在fork()之后,子进程不继承父进程的锁和警告,别的基本上都会继承,而vfork()与fork()不同的地方在于vfork()之后的进程会共享父进程的地址空 ...
- 进程间通信机制(管道、信号、共享内存/信号量/消息队列)、线程间通信机制(互斥锁、条件变量、posix匿名信号量)
注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...
- node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...
- 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)
上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html 现在有这篇文章: http://blog.cs ...
- linux 互斥锁和条件变量
为什么有条件变量? 请参看一个线程等待某种事件发生 注意:本文是linux c版本的条件变量和互斥锁(mutex),不是C++的. mutex : mutual exclusion(相互排斥) 1,互 ...
随机推荐
- 微信小程序中的bindTap事件(微信小程序开发QQ群:604788754)
bindTap对应的绑定事件, 第一个:wx.navigateTo wx.navigateTo({ url:"../content/content" }) 第二个:wx.redir ...
- UVALive 5840 数学题
DES:给出三种材料A,B,C每种的个数.然后组合AB,BC,AC的利润.问能获得的最大利润是多少. 开始一点思路都没有.然后发现可以枚举其中两种的个数.那么最后一种就确定了.还是感觉很机智. #in ...
- CodeIgniter $this->db->where()的自定义语句写法问题
.自定义字符串:你可以手动的编写子句:$where = "name='Joe' AND status='boss' OR status='active'"; $this->d ...
- 1.1 C++布尔类型(bool)
注意: c++ 中 cout << true << endl; 输出为 1: 布尔类型(bool)是C++新增的一种基本数据类型.在标准的C语言中并未定义bool类型,如果需 ...
- hibernate 各种主键生成策略(转)
http://www.cnblogs.com/kakafra/archive/2012/09/16/2687569.html 1.assigned 主键由外部程序负责生成,在 save() 之前必须指 ...
- html 目录结构
"D:\phpStudy\WWW\tubiao\libchart\libchart\classes\view\chart/../../../images/PoweredBy.png" ...
- Python id
# id()函数可以帮我们查看一个变量的内存地址 a = 10 b = 30 print(id(a)) print(id(b)) lst = ["周杰伦","麻花藤&qu ...
- python中几个实用的文件操作
1. 判断指定目录是否存在: os.path.exists(input_folder) 2. 判断指定目录是不是文件夹 os.path.isdir(input_folder) 3. 判断指定目录是不是 ...
- 强化学习 CartPole实验的一些启发 有没有可能设计一个新的实验呢?(杆子可以向360度方向倾倒,可行吗?)
最近在看强化学习方面的东西,突然想到了这么一个事情,那就是经典的CartPole游戏我们改变一下,或者说升级一下,那么使用强化学习是否能得到不错的效果呢? 原始游戏如图: 一点个人的想法: ===== ...
- 【linux】wifi不能使用的问题
Worked solution (Requirements: kernel >=4.11) : Download driver directory from this repo:https:// ...