C/C++条件变量condition

Condition Variable

适用场景

生产者消费者:

在生产者消费者模型中,生产者和消费者的处理速度往往不一样,当消费者比较快时,消费者要在产品数量变为0时等待生产者;
当生产者比较快时,生产者要在队列满时等待生产者。
所以,如果队列长度不限,则只需要一个条件变量就够了,而如果队列长度有限,则一般需要两个条件变量。例子,可见链接:
https://www.jianshu.com/p/cd9e00c23af9(两个)
https://www.jianshu.com/p/c1dfa1d40f53(一个,因为消费者明显快于生产者)

注意事项

1、notify/signal和wait并没有一一对应的关系。

因为,如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用。
也就是说wait不一定要等待,(其实不等待是最优的情况)。
https://www.cnblogs.com/xudong-bupt/p/6707070.html
https://blog.csdn.net/shichao1470/article/details/89856443
如果两个线程只是为了对某些公共资源的互斥操作,则不应该使用condition,否则会有无限等待的可能出现。
比如:https://blog.csdn.net/a675311/article/details/48973883,这种情况直接用互斥体就行了。

#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
// #include<syscall.h>
#include<string.h>


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void * foo(void *pBuffer)
{
	pthread_mutex_lock(&mutex);
	printf("foo start.
");
	strcpy((char*)pBuffer,"hello");//主线程的栈数据在此被修改
	sleep(3);
	printf("buf handled.
");
	pthread_mutex_unlock(&mutex);
	//    pthread_cond_signal(&cond);
	printf("foo end.
");
	sleep(5);
	printf("foo end2.
");
}

int main(int n, char *arg[])
{
	char Buffer[20] = {'1', '2', '3'}; //主线程的栈数据
	pthread_t pthread_id1;
	pthread_create(&pthread_id1,NULL,foo,(void *)Buffer);
	sleep(10);
	pthread_mutex_lock(&mutex);
	fprintf(stdout, "%s, %s, %d
", Buffer, __func__, __LINE__);// 输出数据Buffer到标准输出
//    pthread_cond_wait(&cond,&mutex);//注意pthread_cond_wait()的使用方法
	pthread_mutex_unlock(&mutex);
	fprintf(stdout, "%s, %s, %d
", Buffer, __func__, __LINE__);// 输出数据Buffer到标准输出
	sleep(1);
	fprintf(stdout, "%s, %s, %d
", Buffer, __func__, __LINE__);// 输出数据Buffer到标准输出
	sleep(2);
	fprintf(stdout, "%s, %s, %d
", Buffer, __func__, __LINE__);// 输出数据Buffer到标准输出
	sleep(3);
	fprintf(stdout, "%s, %s, %d
", Buffer, __func__, __LINE__);// 输出数据Buffer到标准输出
	pthread_join(pthread_id1, NULL);
	return 0;
}

2、pthread_cond_signal/notify的两种写法

写法一:
lock(&mutex);
//一些操作
pthread_cond_signal(&cond);
//一些操作
unlock(&mutex);
缺点:在某些线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)回到用户空间,然后pthread_cond_wait返回前需要加锁,但是发现锁没有被释放,又回到内核空间所以一来一回会有性能的问题。
但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。所以Linux中这样用没问题。
写法二:
lock(&mutex);
//一些操作
unlock(&mutex);
pthread_cond_signal(&cond);
优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
缺点:如果unlock之后signal之前,发生进程交换,另一个进程(不是等待条件的进程)拿到这把梦寐以求的锁后加锁操作,那么等最终切换到等待条件的线程时锁被别人拿去还没归还,只能继续等待。
参考链接:https://blog.csdn.net/shichao1470/article/details/89856443

semaphore

介绍

This is one of the oldest synchronization primitives in the history of computer science, invented by the early Dutch computer scientist Edsger W. Dijkstra (he used the names P() and V() instead of acquire() and release()).

A semaphore manages an internal counter which is decremented by each acquire() call and incremented by each release() call. The counter can never go below zero; when acquire() finds that it is zero, it blocks, waiting until some other thread calls release().

适用场景

生产者消费者:

https://blog.csdn.net/weixin_41143631/article/details/89442468
https://blog.csdn.net/zh0314/article/details/77330649
原文地址:https://www.cnblogs.com/JFHS/p/14695239.html