互斥锁和条件变量

  为了允许在线程或进程之间共享数据,同步时必须的,互斥锁和条件变量是同步的基本组成部分。

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 线程的同步 二 (互斥锁和条件变量)的更多相关文章

  1. 【Linux C 多线程编程】互斥锁与条件变量

    一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1) 初始化: 在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化: 对于静态 ...

  2. Linux互斥锁、条件变量和信号量

    Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...

  3. linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁

    Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图:  一.互斥锁(mutex)  锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...

  4. 非常精简的Linux线程池实现(一)——使用互斥锁和条件变量

    线程池的含义跟它的名字一样,就是一个由许多线程组成的池子. 有了线程池,在程序中使用多线程变得简单.我们不用再自己去操心线程的创建.撤销.管理问题,有什么要消耗大量CPU时间的任务通通直接扔到线程池里 ...

  5. 线程私有数据TSD——一键多值技术,线程同步中的互斥锁和条件变量

    一:线程私有数据: 线程是轻量级进程,进程在fork()之后,子进程不继承父进程的锁和警告,别的基本上都会继承,而vfork()与fork()不同的地方在于vfork()之后的进程会共享父进程的地址空 ...

  6. 进程间通信机制(管道、信号、共享内存/信号量/消息队列)、线程间通信机制(互斥锁、条件变量、posix匿名信号量)

    注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...

  7. node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...

  8. 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

    上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html   现在有这篇文章: http://blog.cs ...

  9. linux 互斥锁和条件变量

    为什么有条件变量? 请参看一个线程等待某种事件发生 注意:本文是linux c版本的条件变量和互斥锁(mutex),不是C++的. mutex : mutual exclusion(相互排斥) 1,互 ...

随机推荐

  1. 微信小程序中的bindTap事件(微信小程序开发QQ群:604788754)

    bindTap对应的绑定事件, 第一个:wx.navigateTo wx.navigateTo({ url:"../content/content" }) 第二个:wx.redir ...

  2. UVALive 5840 数学题

    DES:给出三种材料A,B,C每种的个数.然后组合AB,BC,AC的利润.问能获得的最大利润是多少. 开始一点思路都没有.然后发现可以枚举其中两种的个数.那么最后一种就确定了.还是感觉很机智. #in ...

  3. CodeIgniter $this->db->where()的自定义语句写法问题

    .自定义字符串:你可以手动的编写子句:$where = "name='Joe' AND status='boss' OR status='active'"; $this->d ...

  4. 1.1 C++布尔类型(bool)

    注意: c++ 中 cout << true << endl;  输出为 1: 布尔类型(bool)是C++新增的一种基本数据类型.在标准的C语言中并未定义bool类型,如果需 ...

  5. hibernate 各种主键生成策略(转)

    http://www.cnblogs.com/kakafra/archive/2012/09/16/2687569.html 1.assigned 主键由外部程序负责生成,在 save() 之前必须指 ...

  6. html 目录结构

    "D:\phpStudy\WWW\tubiao\libchart\libchart\classes\view\chart/../../../images/PoweredBy.png" ...

  7. Python id

    # id()函数可以帮我们查看一个变量的内存地址 a = 10 b = 30 print(id(a)) print(id(b)) lst = ["周杰伦","麻花藤&qu ...

  8. python中几个实用的文件操作

    1. 判断指定目录是否存在: os.path.exists(input_folder) 2. 判断指定目录是不是文件夹 os.path.isdir(input_folder) 3. 判断指定目录是不是 ...

  9. 强化学习 CartPole实验的一些启发 有没有可能设计一个新的实验呢?(杆子可以向360度方向倾倒,可行吗?)

    最近在看强化学习方面的东西,突然想到了这么一个事情,那就是经典的CartPole游戏我们改变一下,或者说升级一下,那么使用强化学习是否能得到不错的效果呢? 原始游戏如图: 一点个人的想法: ===== ...

  10. 【linux】wifi不能使用的问题

    Worked solution (Requirements: kernel >=4.11) : Download driver directory from this repo:https:// ...