应用Windows信号量机制实现进程(线程)的同步与互斥
- 掌握利用信号量机制实现进程同步互斥的工作原理;
- 学习Windows环境中的多线程并发执行机制。
第一部分:Windows提供的有关线程和信号量机制的函数和API
本篇主要用到了C++11的并发库std::thread、std::mutex、std::future、std::condition_variable、std::atomic。
std::thread
- 构造函数:
(1)default:
默认构造函数,创建一个空的thread执行对象。
thread() noexcept;
(2)initialization:
初始化构造函数,创建一个可以被joinable的thread执行对象。
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
(3)copy[deleted]:
拷贝构造函数被禁用。
thread (const thread&) = delete;
(4)move:
move构造函数,调用成功后不代表任何thread执行对象。
thread (thread&& x) noexcept;
- move赋值操作:
(1)move:
赋值操作。如果当前对象不可joinable,需要传递右值引用;如果可被joinable,则terminate报错。
thread& operator= (thread&& rhs) noexcept;
(2)copy[deleted]:
拷贝赋值操作被禁用。
thread& operator= (const thread&) = delete;
- 其它成员函数:
· get_id
获取线程的id。
· joinable
检查线程是否可被join。
· join
Join线程。
· detach
Detach线程。
· swap
Swap线程。
· native_handle
返回native handle。
· hardware_concurrency [static]
检查硬件并发特性。
std::mutex
Mutex系列类:
· std::mutex 最基本的Mutex类
· std::recursive_mutex 递归Mutex类
· std::time_mutex 定时Mutex类
· std::recursive_timed_mutex 定时递归的Mutex类
Lock类:
· std::lock_guard 方便线程对互斥量上锁。
· std::unique_lock 方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
其他类型类:
· std::once_flag
· std::adopt_lock_t
· std::defer_lock_t
· std::try_to_lock_t
函数:
std::try_lock 尝试同时对多个互斥量上锁。
std::lock 可以同时对多个互斥量上锁。
std::call_once 如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数都只调用一次。
第二部分:生产者-消费者问题在Windows环境下的C语言实现
创建n个线程模拟生产者和消费者,并实现进程(线程)的同步与互斥
- 一个生产者和一个消费者,对n个资源进行读取和写入操作。
#include <windows.h>
#include <cstdlib>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
const int repositorySize = 10;//产品缓冲区的最大容量
const int itemSize = 200;//生产产品的最大数量
struct ItemRepository {
int item_buffer[repositorySize];//产品缓冲区
size_t read_pos = 0;//消费者读取产品的位置 初始化为0
size_t write_pos = 0;//生产者写入产品的位置 初始化为0
std::mutex mtx;//互斥量 实现产品缓冲区的互斥
std::condition_variable repo_not_empty;//条件变量 表示产品缓冲区不为空
std::condition_variable repo_not_full;//条件变量 表示产品缓冲区不为满
}itemRepository;
void ProduceItem(ItemRepository* repo, int item) {//生产资源
//进入区
std::unique_lock<std::mutex> lock(repo->mtx);//上锁
while ((repo->write_pos + 1) % repositorySize == repo->read_pos) {//产品缓冲区已满
std::cout << "生产者正在等待缓冲区内产品被消耗\n";
(repo->repo_not_full).wait(lock);//生产者等待“产品缓冲区不为满”这一条件发生
}
//临界区
repo->item_buffer[repo->write_pos] = item;//写入
repo->write_pos = (repo->write_pos + 1) % repositorySize;
//退出区
repo->repo_not_empty.notify_all();//通知
lock.unlock();
}
int ConsumeItem(ItemRepository* repo) {//消费资源
int item;
//进入区
std::unique_lock<std::mutex> lock(repo->mtx);//上锁
while (repo->write_pos == repo->read_pos) {//产品缓冲区为空
std::cout << "消费者正在等待缓冲区内生产出产品\n";
repo->repo_not_empty.wait(lock);//消费者等待“产品缓冲区不为空”这一条件发生
}
//临界区
item = repo->item_buffer[repo->read_pos];//读取
repo->read_pos = (repo->read_pos + 1) % repositorySize;
//退出区
repo->repo_not_full.notify_all();//通知
lock.unlock();
return item;
}
void Produce() {//生产者
for (int i = 1; i <= itemSize; i++) {
Sleep(1);
std::cout << "正在生产资源\n";
ProduceItem(&itemRepository, i);
}
}
void Consume() {//消费者
for (int i = 1; i <= itemSize; i++) {
Sleep(1);
int item = ConsumeItem(&itemRepository);
std::cout << "正在消费资源\n";
}
}
int main() {
std::thread producer(Produce);
std::thread consumer(Consume);
producer.join();
consumer.join();
return 0;
}
- n个生产者和n个消费者,对n个资源进行读取和写入操作。
#pragma warning (disable:4996)
#include <windows.h>
#include <cstdlib>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
const int repositorySize = 5;//产品缓冲区的最大容量
const int itemSize = 20;//生产产品的最大数量
struct ItemRepository {
int item_buffer[repositorySize];//产品缓冲区
size_t read_pos = 0;//消费者读取产品的位置 初始化为0
size_t write_pos = 0;//生产者写入产品的位置 初始化为0
size_t produced_cnt = 0;//生产者计数
size_t consumed_cnt = 0;//消费者计数
std::mutex mtx;//互斥量 实现产品缓冲区的互斥
std::mutex producer_mutex;//互斥量 实现生产者的互斥
std::mutex consumer_mutex;//互斥量 实现消费者的互斥
std::condition_variable repo_not_empty;//条件变量 表示产品缓冲区不为空
std::condition_variable repo_not_full;//条件变量 表示产品缓冲区不为满
}itemRepository;
void ProduceItem(ItemRepository* repo, int item) {//生产资源
//进入区
std::unique_lock<std::mutex> lock(repo->mtx);//上锁
while ((repo->write_pos + 1) % repositorySize == repo->read_pos) {//产品缓冲区已满
std::cout << "生产者正在等待缓冲区内产品被消耗\n";
(repo->repo_not_full).wait(lock);//生产者等待“产品缓冲区不为满”这一条件发生
}
//临界区
repo->item_buffer[repo->write_pos] = item;//写入
repo->write_pos = (repo->write_pos + 1) % repositorySize;
//退出区
repo->repo_not_empty.notify_all();//通知
lock.unlock();
}
int ConsumeItem(ItemRepository* repo) {//消费资源
int item;
//进入区
std::unique_lock<std::mutex> lock(repo->mtx);//上锁
while (repo->write_pos == repo->read_pos) {//产品缓冲区为空
std::cout << "消费者正在等待缓冲区内生产出产品\n";
repo->repo_not_empty.wait(lock);//消费者等待“产品缓冲区不为空”这一条件发生
}
//临界区
item = repo->item_buffer[repo->read_pos];//读取
repo->read_pos = (repo->read_pos + 1) % repositorySize;
//退出区
repo->repo_not_full.notify_all();//通知
lock.unlock();
return item;
}
void Produce() {//生产者
bool ready_to_exit = false;
for (int i = 1; i <= itemSize; i++) {
Sleep(1);
std::unique_lock<std::mutex> lock(itemRepository.producer_mutex);
if (itemRepository.produced_cnt < repositorySize) {
itemRepository.produced_cnt++;
ProduceItem(&itemRepository, itemRepository.produced_cnt);
std::cout << "正在生产资源\n";
}
else
ready_to_exit = true;
lock.unlock();
if (ready_to_exit)
break;
}
}
void Consume() {//消费者
bool ready_to_exit = false;
for (int i = 1; i <= itemSize; i++) {
Sleep(1);
std::unique_lock<std::mutex> lock(itemRepository.consumer_mutex);
if (itemRepository.consumed_cnt < repositorySize) {
int item = ConsumeItem(&itemRepository);
itemRepository.consumed_cnt++;
std::cout << "正在消费资源\n";
}
else
ready_to_exit = true;
lock.unlock();
if (ready_to_exit)
break;
}
}
int main() {
std::thread producer1(Produce);
std::thread producer2(Produce);
std::thread producer3(Produce);
std::thread producer4(Produce);
std::thread consumer1(Consume);
std::thread consumer2(Consume);
std::thread consumer3(Consume);
std::thread consumer4(Consume);
producer1.join();
producer2.join();
producer3.join();
producer4.join();
consumer1.join();
consumer2.join();
consumer3.join();
consumer4.join();
return 0;
}
第三部分:读者-写者问题在Windows环境下的C语言实现
读者优先
#pragma once
#include<mutex>
#include<condition_variable>
#include<iostream>
#include<vector>
#include<thread>
#include <windows.h>
struct ReadWriteLock {
int readWaiting = 0; //等待读
int writeWaiting = 0; //等待写
int reading = 0; //正在读
int writing = 0; //正在写
std::mutex mx;
std::condition_variable cond;
}readwriteLock;
void readLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->readWaiting++;
while (readwriteLock->writing > 0) {//读者优先
std::cout << "读进程正在等待\n";
readwriteLock->cond.wait(lock);
}
readwriteLock->reading++;
readwriteLock->readWaiting--;
}
void writeLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->writeWaiting++;
while (readwriteLock->reading > 0 || readwriteLock->writing > 0) {
std::cout << "写进程正在等待\n";
readwriteLock->cond.wait(lock);
}
readwriteLock->writing++;
readwriteLock->writeWaiting--;
}
void readUnLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->reading--;
if (readwriteLock->reading <= 0)
readwriteLock->cond.notify_one();//当前没有读者时,唤醒一个写者
}
void writeUnLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->writing--;
readwriteLock->cond.notify_all();//唤醒所有读者、写者
}
void Reader() {
readLock(&readwriteLock);
std::cout << "读进程正在执行\n";
Sleep(1);
readUnLock(&readwriteLock);
}
void Writer() {
writeLock(&readwriteLock);
std::cout << "写进程正在执行\n";
Sleep(1);
writeUnLock(&readwriteLock);
}
int main() {
std::thread reader1(Reader);
std::thread reader2(Reader);
std::thread reader3(Reader);
std::thread reader4(Reader);
std::thread writer1(Writer);
std::thread writer2(Writer);
std::thread writer3(Writer);
std::thread writer4(Writer);
reader1.join();
reader2.join();
reader3.join();
reader4.join();
writer1.join();
writer2.join();
writer3.join();
writer4.join();
return 0;
}
写者优先
#pragma once
#include<mutex>
#include<condition_variable>
#include<iostream>
#include<vector>
#include<thread>
#include <windows.h>
struct ReadWriteLock {
int readWaiting = 0; //等待读
int writeWaiting = 0; //等待写
int reading = 0; //正在读
int writing = 0; //正在写
std::mutex mx;
std::condition_variable cond;
}readwriteLock;
void readLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->readWaiting++;
while (readwriteLock->writing > 0 || readwriteLock->writeWaiting > 0) {//写者优先
std::cout << "读进程正在等待\n";
readwriteLock->cond.wait(lock);
}
readwriteLock->reading++;
readwriteLock->readWaiting--;
}
void writeLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->writeWaiting++;
while (readwriteLock->reading > 0 || readwriteLock->writing > 0) {
std::cout << "写进程正在等待\n";
readwriteLock->cond.wait(lock);
}
readwriteLock->writing++;
readwriteLock->writeWaiting--;
}
void readUnLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->reading--;
if (readwriteLock->reading <= 0)
readwriteLock->cond.notify_one();//当前没有读者时,唤醒一个写者
}
void writeUnLock(ReadWriteLock* readwriteLock) {
std::unique_lock<std::mutex>lock(readwriteLock->mx);
readwriteLock->writing--;
readwriteLock->cond.notify_all();//唤醒所有读者、写者
}
void Reader() {
readLock(&readwriteLock);
std::cout << "读进程正在执行\n";
Sleep(1);
readUnLock(&readwriteLock);
}
void Writer() {
writeLock(&readwriteLock);
std::cout << "写进程正在执行\n";
Sleep(1);
writeUnLock(&readwriteLock);
}
int main() {
std::thread reader1(Reader);
std::thread reader2(Reader);
std::thread reader3(Reader);
std::thread reader4(Reader);
std::thread writer1(Writer);
std::thread writer2(Writer);
std::thread writer3(Writer);
std::thread writer4(Writer);
reader1.join();
reader2.join();
reader3.join();
reader4.join();
writer1.join();
writer2.join();
writer3.join();
writer4.join();
return 0;
}