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,互 ...
随机推荐
- HDU 4091 Zombie’s Treasure Chest 分析 难度:1
Zombie’s Treasure Chest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
- poj2895
题解: splay,维护当前第k大 并查集维护当前集合 合并x,y时,del(num[x]),del(num[y]),insert(num[x]+num[y]) 代码: #include<cst ...
- DBGridEh 在粘贴中文时出现乱码和错位 100zhx_888]
http://www.fx114.net/qa-29-3439.aspx 回复于: -- :: unit DBGridEh; 把下面这个函数替换成这样 procedure TDBGridInplace ...
- apache中的RewriteCond、RewriteRule
Rewirte主要的功能就是实现URL的跳转.可基于服务器级的(httpd.conf)和目录级的(.htaccess) 两种方式.如果要想用到rewrite模块,必须先安装或加载rewrite模块. ...
- Ubuntu:火狐浏览器加速下载(Flashgot+Aria2+Uget)
火狐浏览器加速下载 应用到的程序 Aria2 Uget firefox插件Flashgot 1.下载Uget sudo apt-get install uget 2. 下载Aria2 sudo apt ...
- 用tornado实现一个简单的websocket样例
想用SPRING MVC,NODE.JS EXPRESS,TORNADO实现同一个功能,开阔一下视野. 先来TORNADO的吧.. 客户端代码都差不多,主要是服务端代码. TORNADO的说法: ht ...
- E - An Awful Problem 求两段时间内满足条件的天数//lxm
In order to encourage Hiqivenfin to study math, his mother gave him a sweet candy when the day of th ...
- 解决Android adjustresize全屏无效问题
最近在做一个即时通信的聊天页面时,页面要求要全屏显示,这个时候android:windowSoftInputMode = "adjustResize"属性就不起作用了,如果使用an ...
- can-utils源码解析cansend
前言 本文主要介绍socketCan中的发送函数cansend的源码解析. 代码 /* * cansend.c - simple command line tool to send CAN-frame ...
- ITelephony.aidl
在src下先建立包名为com.android.internal.telephony(右键src > new > package,create package-info.java打钩),然后 ...