<三>线程间同步通信-生产者消费者模型

多线程编程两个问题

1:线程互斥问题
竞态条件->临界区代码段->原子操作->互斥锁mutex

2:线程间的同步通信
生产者,消费者 线程模型

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx2;
std::condition_variable  cvcondition;


class MyQueue2 {

public:


	//生成者 放入元素
	void put(int val) {
	   
		std::unique_lock<std::mutex> lock(mtx2);

		//队里中还有元素,那么不再放入元素,让自己进入等待状态,并放弃锁
		while (mq.empty() == false) {
			//.wait  使得生产者线程进入 1:等待状态 2:把mtx互斥锁释放掉
			cvcondition.wait(lock);			
		}

		std::cout << "生产元素" << val << std::endl;
		mq.push(val);
		//通知其他所有的线程 由让其他所有的线程从等待转入阻塞
		//其他线程收到了该通知后,会有等待状态=>阻塞状态, 进入阻塞状态后就可以去抢夺锁了
		cvcondition.notify_all();
		
                //lock 出了 作用域后自动释放
	}

	//消费者取出元素
	void pop() {
	
		std::unique_lock<std::mutex> lock(mtx2);
		while (mq.empty()) {
			//消费者队中没有元素,让自己进入等待状态,并放弃锁 1:进入等待状态 2:放弃锁
			cvcondition.wait(lock);
		}	
		int get = mq.front();
		mq.pop();
		std::cout << "消费元素" << get << std::endl;			
		//通知其他所有线程 让其他线程可以由等待转入阻塞
		//其他线程收到了该通知后,会有等待状态=>阻塞状态, 进入阻塞状态后就可以去抢夺锁了
		cvcondition.notify_all();

	        //lock 出了 作用域后自动释放
	}


private:
	std::queue<int>  mq;
};


void Producer2(MyQueue2 *mq) {

	for(int i=0;i<10;i++){
		mq->put(i);
	}

}
void Comsumer2(MyQueue2 *mq) {
	
	for (int i = 0; i<10; i++) {
		mq->pop();
	}

}

int main() {

	MyQueue2 mq;
	//开启两个线程 生产者线程和 消费者线程
	std::thread th1(Producer2, &mq);
	std::thread th2(Comsumer2, &mq);

	th1.join();
	th2.join();

	system("pause");
	return 0;
}


补充说明

条件变量
条件变量std::condition_variable的作用是阻塞线程,然后等待通知将其唤醒。我们可以通过某个函数判断是否符合某种条件来决定是阻塞线程等待通知还是唤醒线程,由此实现线程间的同步。
所以简单来说condition_variable的作用就两个——等待(wait)、通知(notify)。下面详细介绍一下

等待线程
condition_variable的等待函数有三个,分别是wait()——等待、wait_for()——等待一段时间、wait_until()——等待至某一时刻。
另外针对每个函数condition_variable还提供了有条件等待和无条件等待两种方式。下面具体讲解一下。
wait()的是普通的等待。分为有条件的等待和无条件的等待
| void wait (unique_lock& lck) | 无条件的等待 |

| void wait (unique_lock& lck, Predicate pred) | 有条件的等待 |

void wait (unique_lock& lck)会无条件的阻塞当前线程然后等待通知,前提是此时对象lck已经成功获取了锁。
等待时会调用lck.unlock()释放锁,使其它线程可以获取锁。
一旦得到通知(由其他线程显式地通知),函数就会释放阻塞并调用lck.lock(),使lck保持与调用函数时相同的状态。
然后函数返回,注意,最后一次lck.lock()的调用可能会在返回前再次阻塞线程

wati()函数因为没有条件判断,因此有时候会产生虚假唤醒,而有条件的等待可以很好的解决这一问题。
void wait (unique_lock& lck, Predicate pred)为有条件的等待,pred是一个可调用的对象或函数,它不接受任何参数,并返回一个可以作为bool计算的值。
当pred为false时wait()函数才会使线程等待,在收到其他线程通知时只有当pred返回true时才会被唤醒。

posted @ 2022-12-13 15:47  Hello_Bugs  阅读(34)  评论(0编辑  收藏  举报