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. c++ 互斥量和条件变量

    线程同步时会遇到互斥量和条件变量配合使用的情况,下面看一下C++版的. test.h #include <pthread.h> #include <iostream>class ...

  3. Linux多线程同步的几种方式

    线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点.linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 1)互斥锁(mutex) 通过锁机制实现线程间的 ...

  4. 数据共享之相互排斥量mutex

    相互排斥量介绍 相互排斥量能够保护某些代码仅仅能有一个线程运行这些代码.假设有个线程使用相互排斥量运行某些代码,其它线程訪问是会被堵塞.直到这个线程运行完这些代码,其它线程才干够运行. 一个线程在訪问 ...

  5. linux多线程同步概览

    linux多线程同步概览 临界区 互斥锁 mutex 基本函数 pthread_mutex_destroy 何时调用? 互斥锁类型? 互斥量和自旋锁的区别 条件变量 condition variabl ...

  6. 多任务的同步与相互排斥

    现代操作系统基本都是多任务操作系统,即同一时候有大量可调度实体在执行.在多任务操作系统中,同一时候执行的多个任务可能: 都须要訪问/使用同一种资源 多个任务之间有依赖关系.某个任务的执行依赖于还有一个 ...

  7. 线程同步与相互排斥:相互排斥锁

    为什么须要相互排斥锁? 在多任务操作系统中,同一时候执行的多个任务可能都须要使用同一种资源.这个过程有点类似于,公司部门里.我在使用着打印机打印东西的同一时候(还没有打印完).别人刚好也在此刻使用打印 ...

  8. 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)

    互斥量.条件变量与pthread_cond_wait()函数的使用,详解(二) 1.Linux"线程" 进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线 ...

  9. C++11学习笔记-----互斥量以及条件变量的使用

    在多线程环境中,当多个线程同时访问共享资源时,由于操作系统CPU调度的缘故,经常会出现一个线程执行到一半突然切换到另一个线程的情况.以多个线程同时对一个共享变量做加法运算为例,自增的汇编指令大致如下, ...

  10. Linux 多线程同步机制:互斥量、信号量、条件变量

    互斥量:互斥量提供对共享资源的保护访问,它的两种状态:lock和unlock,用来保证某段时间内只有一个线程使用共享资源,互斥量的数据类型是pthread_mutex_t 主要涉及函数:pthread ...

最新文章

  1. matlab-JDBC操作MYSQL数据库中文乱码解决
  2. 如何查看数据库索引的利用率?
  3. WebStorm使用ES6
  4. mysql blob hex_数据库的完整备份与恢复 quot;--hex-blobquot; - - ITeye博客
  5. 现代软件工程 (备份)
  6. x60 深度linux,vivo X60 系列将全球首发 OriginOS 交互体验脱胎换骨
  7. 2016下半年网络规划设计师考试上午真题
  8. setjmp.h(c标准库)
  9. FPGA外挂DDR存储器简介
  10. ArcEngine二次开发_04(鼠标点击图层点显示属性(两个及以上))
  11. java判断回文用valueof_判断字符串是否是回文
  12. 改进我个人知识管理手段
  13. C++ ---------------- 成员函数指针揭秘
  14. MT2503芯片平台方案开发项目资料介绍
  15. (转)Sublime Text 2 设置文件详解
  16. CoreOS容器云企业实战(3)--Docker技术实践
  17. 5.3.1 Unique Binary Sear Trees
  18. scrapy中的Request的用法
  19. 全国计算机考试进制计算器吗,进制数换算器(十六进制计算器在线)
  20. Delphi历史版本介绍(一)从Delphi1到Delphi7

热门文章

  1. 浅谈javascript和java中的字符串
  2. SQL动态配置,动态解析SQL
  3. Struts2.0 xml文件的配置(package,namespace,action)
  4. 物流知识分享——浅析库存日结作业
  5. MS SqL2000 数据库置疑状态的解决方法[转]
  6. 截获webView点击事件
  7. 如何使用TunesKit AceMovi Video Editor Mac在视频中添加贴纸
  8. linux的下载工具
  9. 利用组策略进行软件分发
  10. 《(学习笔记)两天进步一点点》(3)——应用BindingSource实现数据同步