linux线程间通信之条件变量和互斥量

一、条件变量定义

有的时候仅仅依靠锁住共享资源来使用它是不够的。有时候共享资源只有某些状态的时候才能够使用。比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈。这种情况下的同步使用互斥锁是不够的。另一种同步的方式--条件变量,就可以使用在这种情况下。条件变量(Condition Variable)是线程间的一种同步机制,提供给两个线程协同完成任务的一种方法,使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量的测试一般是用互斥量来保护的,用来确保每一时刻只有一个线程能够改变条件变量,如果条件为假,线程通常会基于条件变量而阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:

  • 唤醒
  • 再次获取互斥锁
  • 重新评估条件

在以下情况下,条件变量可用于在进程之间同步线程:

  • 线程是在可以写入的内存中分配的
  • 内存由协作进程共享

Condition Variable用pthread_cond_t类型的变量表示,和Mutex的初始化和销毁类似,pthread_cond_init函数初始化一个Condition Variable,attr参数为NULL则表示缺省属性,pthread_cond_destroy函数销毁一个Condition Variable。如果ConditionVariable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于用pthread_cond_init函数初始化并且attr参数为NULL。

条件变量的相关函数如下:

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
//Both return: 0 if OK, positive Exxx value on error

pthread_cond_wait用于等待某个特定的条件为真,一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:

1. 释放Mutex

2. 阻塞等待

3. 当被唤醒时,重新获得Mutex并返回

注意:3个操作是原子性的操作,之所以一开始要释放Mutex,是因为需要让其他线程进入临界区去更改条件,或者也有其他线程需要进入临界区等待条件。

pthread_cond_signal用于通知阻塞的线程某个特定的条件为真了。在调用者两个函数之前需要声明一个pthread_cond_t类型的变量,用于这两个函数的参数。

为什么条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的呢?因为“某个特性条件”通常是在多个线程之间共享的某个变量。互斥锁允许这个变量可以在不同的线程中设置和检测。

通常,pthread_cond_wait只是唤醒等待某个条件变量的一个线程。如果需要唤醒所有等待某个条件变量的线程,需要调用:

int pthread_cond_broadcast (pthread_cond_t * cptr);

默认情况下面,阻塞的线程会一直等待,知道某个条件变量为真。如果想设置最大的阻塞时间可以调用:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果时间到了,条件变量还没有为真,仍然返回,返回值为ETIME。

二、条件变量使用规范

(一)、等待条件代码

pthread_mutex_lock(&mutex);

while (条件为假)

pthread_cond_wait(cond, mutex);

修改条件

pthread_mutex_unlock(&mutex);

(二)、给条件发送通知代码

pthread_mutex_lock(&mutex);

设置条件为真

pthread_cond_signal(cond);

pthread_mutex_unlock(&mutex);

至于为什么在被唤醒之后还要再次进行条件判断(即为什么要使用while循环来判断条件),是因为可能有“惊群效应”。有人觉得此处既然是被唤醒的,肯定是满足条件了,其实不然。如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。考虑以下情况:

1,pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,while循环的意义就体现在这里了,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.

2,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐此处使用while循环.

其实说白了很简单,就是pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话,就必须要使用while来进行条件判断,以保证临界区内只有一个线程在处理。

一个例子如下:

<span style="font-size:14px;">#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>

typedef void* (*fun)(void*);

int x=1,y=4;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* thread1(void*);
void* thread2(void*);

int main(int argc, char** argv)
{
	printf("enter main!\n");
	pthread_t tid1, tid2;
	int rc1=0, rc2=0;
	rc2 = pthread_create(&tid2, NULL, thread2, NULL);
	if(rc2 != 0)
		printf("%s: %d\n",__func__, strerror(rc2));

	rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
	if(rc1 != 0)
		printf("%s: %d\n",__func__, strerror(rc1));
	sleep(1);
	printf("leave main!\n");
	exit(0);
}

void* thread1(void* arg)
{
	printf("enter thread1\n");
	printf("this is thread1: x= %d,y=%d. thread id is %u\n",x,y, (unsigned int)pthread_self());
	pthread_mutex_lock(&mutex);
	x+=y;
	if(x>y)
		pthread_cond_signal(&cond);

	printf("this is thread1: x= %d,y=%d. thread id is %u\n", x,y, (unsigned int)pthread_self());
	pthread_mutex_unlock(&mutex);
	pthread_join(*(pthread_t*)arg, NULL);
	printf("leave thread1\n");
	pthread_exit(0);
}

void* thread2(void* arg)
{
	printf("enter thread2\n");
	printf("this is thread2: x= %d,y=%d. thread id is %u\n", x,y, (unsigned int)pthread_self());
	pthread_mutex_lock(&mutex);
	while(x<=y)
		pthread_cond_wait(&cond, &mutex);
	x-=y;
	printf("this is thread2: x= %d,y=%d. thread id is %u\n", x,y, (unsigned int)pthread_self());
	pthread_mutex_unlock(&mutex);
	printf("leave thread2\n");
	pthread_exit(0);
}
</span>

编译时加上-lpthread参数即可。

linux线程间通信之条件变量和互斥量

时间: 2024-08-27 07:15:30

linux线程间通信之条件变量和互斥量的相关文章

使用线程间通信之条件变量

最近用C++写安卓下的一个通讯程序,作为jni库给java调用,采用多线程轮询遇到一个问题描述如下: A线程收到数据,放入队列,是生产者. B.C.D若干个线轮询训消息队列,如果队列有数据就取出进行处理,没数据就Sleep(T)休息,问题是这个T值取多大合适?取大了消息处理不及时,取小了手机cpu上升电池很快耗光. 这个问题最佳解法是采用条件变量,可以比较完美解决问题,以下代码使用C++封装,用win32 SDK的条件变量举例,Linux下有完全等价的概念: // 线程消息通知 class Th

进程间同步(1)&mdash;&mdash;条件变量和互斥量

1. 概述 条件变量和互斥量是最基本的同步形式,总是用于同步同一个进程的各个线程间同步. 当把条件变量或互斥量放在共享内存区时,可用于进程间同步. 同样的情况还有读写锁,它们都是随进程的持续性.   2.互斥锁 互斥锁指代相互排斥,用于保护临界区.多个线程和多个进程分享的共享数据. 静态初始化:static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 动态初始化:互斥锁是动态分配的,pthread_mutex_init(&mutex);初始

条件变量与互斥量

看了很多文档,就简书说的最好. 关键词: 无竞争等待.同步机制(类似ABCABCABC).条件变量不是锁.线程阻塞.pthread_cond_wait 我理解是一个动作. 概念 线程同步的方法有多种,互斥量.信号量.条件变量.读写锁等.互斥量在允许或阻塞对临界区的访问上是很有效的,线程是在对已加锁的互斥量加锁时发生阻塞:条件变量则允许线程由于一些未达到的条件而阻塞,此处的"条件"可以由用户来定义,在访问该条件时需要加锁(互斥量),如果条件没达到,线程将阻塞在该条件上. 条件变量特别适用

浅析线程间通信三:Barriers、信号量(semaphores)以及各种同步方法比较

之前的文章讨论了互斥量.条件变量.读写锁和自旋锁用于线程的同步,本文将首先讨论Barriers和信号量的使用,并给出了相应的代码和注意事项,相关代码也可在我的github上下载,然后对线程各种同步方法进行了比较. Barriers Barriers是一种不同于前面线程同步机制,它主要用于协调多个线程并行(parallel)共同完成某项任务.一个barrier对象可以使得每个线程阻塞,直到所有协同(合作完成某项任务)的线程执行到某个指定的点,才让这些线程继续执行.前面使用的pthread_join

关于eventfd,epoll,线程间通信小记

先介绍eventfd 1 #include<sys/eventfd.h> 2 int eventfd(unsigned int initval, int flags); 使用这个函数来创建一个事件对象,linux线程间通信为了提高效率,大多使用异步通信,采用事件监听和回调函数的方式来实现高效的任务处理方式(虽然会将逻辑变得复杂). linux内核会为这个事件对象维护一个64位的计数器(uint64_t).并在初始化时用传进去的initval来初始化这个计数器,然后返回一个文件描述符来代表这个事

浅析线程间通信一:互斥量和条件变量

线程同步的目的简单来讲就是保证数据的一致性.在Linux中,常用的线程同步方法有互斥量( mutex ).读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题.另外还有自旋锁.barrier和信号量线程同步方法.本文将讨论互斥量和条件变量的使用,并给出了相

进程间通信与线程间通信

序 今天被问及进程间通信的问题,发现自己了解的并不够,所以,对此好好总结一番~ 操作系统的主要任务是管理计算机的软件.硬件资源.现代操作系统的主要特点是多用户和多任务,也就是程序的并行执行,windows如此linux也是如此.所以操作系统就借助于进程来管理计算机的软.硬件资源,支持多任务的并行执行.要并行执行就需要多进程.多线程.因此多进程和多线程间为了完成一定的任务,就需要进行一定的通信.而线程间通信又和进程间的通信不同.由于进程的数据空间相对独立而线程是共享数据空间的,彼此通信机制也很不同

线程间通信和线程互斥

线程间通信 1> 线程间通信分为两种 主线程进入子线程(前面的方法都可以) 子线程回到主线程 2> 返回主线程 3> 代码 这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后在主线程刷新UI界面 视图布局我就不写了,大家自己来吧,线程间通信代码如下: #pragma mark - 添加响应方法触发创建子线程并加载数据 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

Linux多线程实践(8) --Posix条件变量解决生产者消费者问题

Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond