1. 什么是相互排斥量

相互排斥量从本质上说是一把锁,在訪问共享资源前对相互排斥量进行加锁,在訪问完毕后释放相互排斥量上的锁。

对相互排斥量进行加锁以后,不论什么其它试图再次对相互排斥量加锁的线程将会被堵塞直到当前线程释放该相互排斥锁。假设释放相互排斥锁时有多个线程堵塞,所以在该相互排斥锁上的堵塞线程都会变成可进行状态。第一个变成执行状态的线程能够对相互排斥量加锁。其它线程在次被堵塞,等待下次执行状态。

pthread_mutex_t 就是POSIX对于mutex的实现。

函数名 參数 说明
pthread_mutex_init

pthread_mutex_t * mutex,

constpthread_mutex_t *attr

初始化一个相互排斥量,静态方式能够直接使用PTHREAD_MUTEX_INITIALIZER进行赋值初始化
pthread_mutex_destroy pthread_mutex_t *mutex 释放对相互排斥变量分配的资源。注意pthread_mutex_init有可能malloc了资源
pthread_mutex_lock pthread_mutex_t *mutex 假设相互排斥量已经上锁,调用线程堵塞直至相互排斥量解锁
pthread_mutex_trylock pthread_mutex_t *mutex 加锁。假设失败不堵塞
pthread_mutex_unlock pthread_mutex_t *mutex 解锁

使用init函数进行初始化:

[cpp] view
plain
 copy

  1. #include <pthread.h>
  2. pthread_mutex_t foo_mutex;
  3. void foo()
  4. {
  5. pthread_mutex_init(&foo_mutex, NULL);
  6. pthread_mutex_lock(&foo_mutex);
  7. /* Do work. */
  8. pthread_mutex_unlock(&foo_mutex);
  9. pthread_mutex_destroy(&foo_mutex);
  10. }

当然该初始化

[cpp] view
plain
 copy

  1. pthread_mutex_init(&foo_mutex, NULL);

仅仅能foo_mutex使用前初始化一次。最后destroy。

初始化已经初始化的mutex将导致undefined behavior。

第二种使用方法:

[cpp] view
plain
 copy

  1. pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;
  2. void foo()
  3. {
  4. pthread_mutex_lock(&foo_mutex);
  5. /* Do work. */
  6. pthread_mutex_unlock(&foo_mutex);
  7. }

当然了,这两种使用方法都有问题:假设在lock住后unlock之前出现exception,那么这个锁永远也不能unlock。

这样的情况下须要guard这个资源。详细可參照boost::mutex::scoped_lock的实现,很easy可是极大简化了mutex的安全使用。

2. 什么是条件变量

与相互排斥锁不同,条件变量是用来等待而不是用来上锁的。

条件变量用来自己主动堵塞一个线程,直到某特殊情况发生为止。通常条件变量和相互排斥锁同一时候使用。

条件变量使我们能够睡眠等待某种条件出现。

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包含两个动作:一个线程等待"条件变量的条件成立"而挂起;还有一个线程使"条件成立"(给出条件成立信号)。

条件的检測是在相互排斥锁的保护下进行的。

假设一个条件为假,一个线程自己主动堵塞,并释放等待状态改变的相互排斥锁。假设还有一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程。又一次获得相互排斥锁,又一次评价条件。假设两进程共享可读写的内存,条件变量能够被用来实现这两进程间的线程同步。

条件变量的初始化和mutex的初始化差点儿相同,也是有两种方式:

pthread_cond_tmy_condition=PTHREAD_COND_INITIALIZER;

也能够利用函数pthread_cond_init动态初始化。

以下中各个函数的简单介绍。

函数名 參数 说明
pthread_cond_init pthread_cond_t *cond, 

const pthread_condattr_t *attr
初始化
pthread_cond_destroy pthread_cond_t *cond 回收
pthread_cond_wait pthread_cond_t *cond,

pthread_mutex_t *mutex
等待。无超时
pthread_cond_timedwait pthread_cond_t *cond,pthread_mutex_t *mutex,

const struct timespec *abstime
等待。有超时
pthread_cond_signal pthread_cond_t *cond 一个在同样条件变量上堵塞的线程将被解锁。假设同一时候有多个线程堵塞,则由调度策略确定接收通知的线程
pthread_cond_broadcast pthread_cond_t *cond 将通知堵塞在这个条件变量上的全部线程。一旦被唤醒,线程仍然会要求相互排斥锁。

一个简单使用条件变量进行线程同步的小样例:

/*
* pthread_mutex_cond.c
*
*/ #include <pthread.h>
#include <stdio.h>
#include <stdlib.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int i = 0; void *thread1(void *);
void *thread2(void *); int main(void)
{
pthread_t tid1, tid2;
if(pthread_create(&tid1, NULL, thread1, NULL))
exit(1);
if(pthread_create(&tid2, NULL, thread2, NULL))
exit(1);
if(pthread_join(tid1, NULL))
exit(1);
printf("thread1 exit\n");
if(pthread_join(tid2, NULL))
exit(1);
printf("thread2 exit\n"); pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
} void *thread1(void *arg)
{
printf("thread1 start\n");
while(i <= 6)
{
pthread_mutex_lock(&mutex);
printf("thread1: lock %d\n", __LINE__);
printf("thread1 i = %d\n", i);
if(i%3 == 0)
{
printf("thread1:signal 1 %d\n", __LINE__);
pthread_cond_signal(&cond);
printf("thread1:signal 2 %d\n", __LINE__);
}
pthread_mutex_unlock(&mutex);
printf("thread1: unlock %d\n", __LINE__);
sleep(1); //sleep 1s,让线程2得以运行
i++;
}
pthread_exit((void *)0);
} void *thread2(void *arg)
{
//sleep(1);
printf("thread2 start\n");
while(i <= 6)
{
pthread_mutex_lock(&mutex);
printf("thread2: lock %d\n", __LINE__);
printf("thread2 i = %d\n", i);
if(i%3 != 0)
{
printf("thread2: wait 1 %d\n", __LINE__);
pthread_cond_wait(&cond, &mutex);
printf("thread2: wait 2 %d\n", __LINE__);
} pthread_mutex_unlock(&mutex);
printf("thread2: unlock %d\n", __LINE__);
sleep(1); //sleep 1s。让线程1得以运行
}
pthread_exit((void *)0);
}

运行结果:

thread2 start

thread2: lock 65

thread2 i = 0

thread2: unlock 75

thread1 start

thread1: lock 42

thread1 i = 0

thread1:signal 1  46

thread1:signal 2  48

thread1: unlock 51

thread2: lock 65

thread2 i = 0

thread2: unlock 75

thread1: lock 42

thread1 i = 1

thread1: unlock 51

thread2: lock 65

thread2 i = 1

thread2: wait 1  69

thread1: lock 42

thread1 i = 2

thread1: unlock 51

thread1: lock 42

thread1 i = 3

thread1:signal 1  46

thread1:signal 2  48

thread1: unlock 51

thread2: wait 2  71

thread2: unlock 75

thread2: lock 65

thread2 i = 3

thread2: unlock 75

thread1: lock 42

thread1 i = 4

thread1: unlock 51

thread2: lock 65

thread2 i = 4

thread2: wait 1  69

thread1: lock 42

thread1 i = 5

thread1: unlock 51

thread1: lock 42

thread1 i = 6

thread1:signal 1  46

thread1:signal 2  48

thread1: unlock 51

thread2: wait 2  71

thread2: unlock 75

thread2: lock 65

thread2 i = 6

thread2: unlock 75

thread1 exit

thread2 exit

生产者-消费者的实现


该问题描写叙述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际执行时会发生的问题。
要求:当工作队列为空时,消费者不能从工作队列取走数据,直到工作队列有数据时才干够。
本例中用一个单链表来模拟工作队列。生产者生产一个结构体串在链表的表头上,消费者从表头取走结构体。在两个线程里分别计数。生产者生产6次,消费者消费6次。

/*
* producer-consumer.c
*
*/ #include <stdio.h>
#include <stdlib.h>
#include <pthread.h> struct msg{
struct msg *next;
int num;
}; struct msg *head; //共享资源,全局指针初始化为NULL
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *);
void *producer(void *); int main(void)
{
pthread_t tid1,tid2;
int res; srand(time(NULL));
res = pthread_create(&tid1,NULL,producer,NULL);
if(res != 0)
{
perror("thread producer create failed\n");
exit(1);
}
res = pthread_create(&tid2,NULL,consumer,NULL);
if(res != 0)
{
perror("thread consumer create failed\n");
exit(1);
}
pthread_join(tid1,NULL);
if(res != 0)
{
perror("join thread producer failed\n");
exit(1);
}
printf("thread producer exit\n");
pthread_join(tid2,NULL);
if(res != 0)
{
perror("join thread consumer failed\n");
exit(1);
}
printf("thread consumer exit\n"); pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_product);
exit(0);
} void *producer(void *arg)
{
struct msg *mp;
int i;
printf("producer thread start\n");
for(i=0;i<6;i++)
{
printf("producer i = %d\n", i);
mp = (struct msg *)malloc(sizeof(struct msg));
mp->num = rand()%100 + 1;
printf("Produce %d\n", mp->num);
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp; //生产者生产一个结构体串在链表的表头上
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&has_product);
sleep(1); //让还有一个线程有机会运行
}
pthread_exit(NULL);
} void *consumer(void *arg)
{
struct msg *con;
int i;
printf("consumer thread start\n");
for(i=0;i<6;i++)
{
printf("consumer i = %d\n", i);
pthread_mutex_lock(&mutex);
while(head == NULL)
{
printf("struct msg is null\n");
pthread_cond_wait(&has_product,&mutex);
}
con = head; //消费者从表头取走结构体
head = con->next;
pthread_mutex_unlock(&mutex);
printf("Consume %d\n", con->num);
free(con);
sleep(1); //让还有一个线程有机会运行
}
pthread_exit(NULL);
}

运行结果:

consumer thread start

consumer i = 0

struct msg is null

producer thread start

producer i = 0

Produce 52

Consume 52

consumer i = 1

struct msg is null

producer i = 1

Produce 33

Consume 33

consumer i = 2

struct msg is null

producer i = 2

Produce 77

Consume 77

consumer i = 3

struct msg is null

producer i = 3

Produce 86

Consume 86

consumer i = 4

struct msg is null

producer i = 4

Produce 84

Consume 84

consumer i = 5

struct msg is null

producer i = 5

Produce 46

Consume 46

thread producer exit

thread consumer exit

Linux多线程同步之相互排斥量和条件变量的更多相关文章

  1. Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题

    前面的一片文章我们已经讲过使用信号量解决生产者消费者问题.那么什么情况下我们须要引入条件变量呢? 这里借用  http://www.cnblogs.com/ngnetboy/p/3521547.htm ...

  2. Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁

    相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...

  3. Linux同步与相互排斥应用(零):基础概念

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途]         当操作系统进入多道批处理系统时 ...

  4. linux系统编程:线程同步-相互排斥量(mutex)

    线程同步-相互排斥量(mutex) 线程同步 多个线程同一时候訪问共享数据时可能会冲突,于是须要实现线程同步. 一个线程冲突的演示样例 #include <stdio.h> #includ ...

  5. 【C/C++多线程编程之六】pthread相互排斥量

    多线程编程之线程同步相互排斥量       Pthread是 POSIX threads 的简称,是POSIX的线程标准.          Pthread线程同步指多个线程协调地,有序地同步使用共享 ...

  6. Linux线程相互排斥量--进程共享属性

    多线程中.在相互排斥量和 读写锁的 属性中.都有一个叫 进程共享属性 . 对于相互排斥量,查询和设置这个属性的方法为: pthread_mutexattr_getpshared pthread_mut ...

  7. android NDK编程:使用posix多线程与mutex相互排斥同步

    MainActivity.java 调用原生方法 posixThreads(int threads, int iterations) 启动线程 package com.apress.threads; ...

  8. [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

    一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

  9. μCOS-II系统之事件(event)的使用规则及Semaphore的相互排斥量使用方法

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/wavemcu/article/details/27798493 ****************** ...

随机推荐

  1. win7设置电脑锁屏时间

    方法/步骤 1 小编用的win7电脑,进入控制面板先~ 2 选择系统与安全选项. 3 如图所示,箭头所指,可以设置锁屏时间,不过电源选项中还有个设置开启屏幕输入密码的设置,第一个就是. 4 如图所示, ...

  2. Windows:chm 文件打开出现“已取消到该网页的导航”的解决方案

    症状 解决方案

  3. 树莓派2B安装Xware迅雷远程下载

    转自:http://www.cnblogs.com/liangjh/articles/5347811.html 一.安装使用迅雷Xware (1)下载Xware1.0.31_armel_v5te_gl ...

  4. JS --- reduce()函数

    定义: reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值.对空数组是不会执行回调函数的. 案例 计算数组总和 var num = [1,2,3,4,5 ...

  5. Mybatis对oracle数据库进行foreach批量插入操作

    MySQL支持的语法 INSERT INTO `tableX` ( `a`, `b`, `c`, `d`, `e` ) VALUES <foreach collection ="lis ...

  6. 我们的生活第二季/全集This Is Us迅雷下载

    NBC剧集<我们这一天>宣布一次性续订2.3季,这部Dan Fogelman打造的大热剧是这个秋季档收视人数第二的广播网剧情剧.新续订的两季还是每季18集. NBC的叫好叫座剧<我们 ...

  7. Eclipse断点调试(DBG)Android应用

    1.添加断点 双击左侧边框便可添加断点,右击也能添加断点. 2.进入调试模式 点击虫子,然后选择工程运行,快捷键为单击F11 ,如果是正常运行就是Ctrl+F11 3.单步调试+跳到下一个断点 运行到 ...

  8. [Web 前端 ] ES6 == ES 2015

    cp from : https://www.cnblogs.com/ricoliu/p/5996149.html 遇到了一个困惑   原来称作es6的现在突然变成es2015 了 原因是这个事ecma ...

  9. 我的Java学习推荐书目

    一直有这么个想法,列一下我个人认为在学习和使用Java过程中可以推荐一读的书籍,给初学者或者想深入的朋友一些建议,帮助成长.推荐的的都是我自己读过,也会推荐一些朋友读过并且口碑不错的书籍. 一.基础类 ...

  10. 通过pycharm使用git

    前言 使用git+pycharm有一段时间了,算是稍有点心得,这边整理一下,可能有的方法不是最优,欢迎交流,可能还是习惯敲命令去使用git,不过其实pycharm已经帮忙做了很多了,我们可以不用记住那 ...