买票问题,⽣产者-消费者,哲学家就餐问题, 读者写者问题,理发师问题,三个烟⻤问题,使用原⼦操作,semaphore,condition variable中任⼀种同步原语实现。

开始前遇到了一个问题。程序中include了mutex,但是一直提示没有导入。查看了本地库,确实存在mutex文件。原来是安装mingw时出现了问题,最后参照链接解决。

买票问题

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

#define MAX_TICKET 100
#define MAX_SELLER 4
using namespace std;
mutex mtx;
thread sellers[MAX_SELLER];
condition_variable cv;
int ticketCnt = MAX_TICKET;

void Sell(int id) {
while (true) {
unique_lock<mutex> locker(mtx);
cv.wait(locker, [] { return true; });
if (ticketCnt == 0) break;
cout << "Seller " << id << "\t" << "Ticket " << ticketCnt-- << endl;
cv.notify_all();
this_thread::sleep_for(chrono::milliseconds(200));
}
}

int main() {
for (int i = 0; i < MAX_SELLER; ++i) sellers[i] = thread(Sell, i + 1);
for (auto &seller: sellers) { seller.join(); }
return 0;
}

⽣产者-消费者

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

#define MAX_SIZE 10
#define MAX_OPERATION 100
#define MAX_PRODUCER 3
#define MAX_CONSUMER 3
using namespace std;

mutex mtx;
thread producers[MAX_PRODUCER];
thread consumers[MAX_CONSUMER];
int productCnt = 0, operation = 1;
condition_variable cvConsumer;
condition_variable cvProducer;

void Producer(int id) {
while (true) {
unique_lock<mutex> locker(mtx);
cvProducer.wait(locker, [] { return productCnt != MAX_SIZE || operation > MAX_OPERATION; });
if (operation > MAX_OPERATION) break; //达到操作次数上限
cout << operation << "\t" << "Producer " << id << "\t" << "product " << ++productCnt << endl;
operation++; //操作次数++
cvConsumer.notify_all(); //通知消费者消费
this_thread::sleep_for(chrono::milliseconds(200)); //延时模拟生产过程
}
cout << "Producer " << id << " exits" << endl;
}

void Consumer(int id) {
while (true) {
unique_lock<mutex> locker(mtx);
cvConsumer.wait(locker, [] { return productCnt != 0 || operation > MAX_OPERATION; });
if (operation > MAX_OPERATION) break;
cout << operation << "\t" << "Consumer " << id << "\t" << "product " << productCnt-- << endl;
operation++;
cvProducer.notify_all();
this_thread::sleep_for(chrono::milliseconds(200));
}
cout << "Consumer " << id << " exits" << endl;
}

int main() {
for (int i = 0; i < MAX_PRODUCER; ++i) producers[i] = thread(Producer, i + 1);
for (int i = 0; i < MAX_CONSUMER; ++i) consumers[i] = thread(Consumer, i + 1);
for (auto &producer: producers) { producer.join(); }
for (auto &consumer: consumers) { consumer.join(); }
cout << "products remained: " << productCnt << endl;
return 0;
}

哲学家就餐问题

LeetCode1226,见链接

读者写者问题(读者优先)

#include <iostream>
#include <semaphore.h>
#include <thread>

#define MAX_READER 3
#define MAX_WRITER 1
using namespace std;

thread writers[MAX_WRITER];
thread readers[MAX_READER];
int readerCnt = 0;
sem_t readerSem, w;

void Read(int id, int n) {
for (int i = 0; i < n; ++i) {
sem_wait(&readerSem);
readerCnt++;
if (readerCnt == 1) sem_wait(&w); //first in
cout << "reader " << id << endl;
sem_post(&readerSem);

this_thread::sleep_for(chrono::milliseconds(500)); //延时模拟reading

sem_wait(&readerSem);
readerCnt--;
if (readerCnt == 0) sem_post(&w); //last out
sem_post(&readerSem);
}
}

void Write(int id, int n) {
for (int i = 0; i < n; ++i) {
sem_wait(&w);
cout << "writer " << id << endl;
sem_post(&w);
}
}

int main() {
sem_init(&readerSem, 0, 1);
sem_init(&w, 0, 1);
for (int i = 0; i < MAX_WRITER; ++i) writers[i] = thread(Write, i + 1, 10);
for (int i = 0; i < MAX_READER; ++i) readers[i] = thread(Read, i + 1, 20);
for (auto &writer: writers) writer.join();
for (auto &reader: readers) reader.join();
return 0;
}

理发师问题

description

理发店里有一位理发师,MAX_CHAIR把供顾客等待理发时坐的椅子。如果没有顾客,理发师睡眠,当顾客到来时叫醒理发师。若理发师正在理发时又有顾客来,有空椅子就坐下,否则离开。代码实现一个理发师,MAX_CUSTOMER个顾客的情况。

solution

#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>
#include <semaphore.h>

#define MAX_CHAIR 10
#define MAX_CUSTOMER 20
using namespace std;

int empty_chairs = MAX_CHAIR; // 空椅子数
condition_variable cv_barbers; // 当有顾客到时需通知理发师
mutex bar_mtx;
thread customers[MAX_CUSTOMER], thread_barber;
sem_t chairs;

/**
* 理发师进程,阻塞理发师的请况
* 1. 没有顾客,则睡觉(阻塞)
* 2. 访问chair临界区受阻,此时chair临界区正在被顾客访问
*/
[[noreturn]] void barber() {
while (true) {
unique_lock <mutex> lck(bar_mtx);
cv_barbers.wait(lck, [] { return MAX_CHAIR - empty_chairs > 0; });
sem_wait(&chairs);
empty_chairs++;
cout << "cut hair" << "\t" << empty_chairs << " Empty chair(s) left." << endl;// cut hair
sem_post(&chairs);
// 理发时不断有顾客进来
this_thread::sleep_for(std::chrono::microseconds(10));
}
}

/**
* 顾客进程,阻塞顾客进程的情况有一种
* 1. 访问chair临界区(检查是否有空闲的椅子)时发现理发师进程也在访问临界区,P(chairs)
* 没有多余的椅子并不是阻塞顾客进程,直至有空闲椅子,而是直接离开,即该顾客不排队理发
*/
void customer(int i) {
sem_wait(&chairs);
if (empty_chairs > 0) {
empty_chairs--;
cv_barbers.notify_one();
cout << "customer " << i << " came. " << "\t" << empty_chairs << " Empty chair(s) left." << endl;
} else cout << "customer " << i << " came and left." << endl;
sem_post(&chairs);
}

int main() {
sem_init(&chairs, 0, 1);
thread_barber = thread(barber);
for (int i = 0; i < MAX_CUSTOMER; ++i) customers[i] = thread(customer, i + 1);
thread_barber.join();
for (auto &customer: customers) { customer.join(); }
return 0;
}

使用信号量实现对椅子资源的互斥访问。使用condition_variable实现理发师的休息和唤醒。输出如下例。

customer 2 came. 9 Empty chair(s) left.
cut hair 10 Empty chair(s) left.
customer 3 came. 9 Empty chair(s) left.
customer 1 came. 8 Empty chair(s) left.
customer 14 came. 7 Empty chair(s) left.
customer 5 came. 6 Empty chair(s) left.
customer 6 came. 5 Empty chair(s) left.
customer 7 came. 4 Empty chair(s) left.
customer 8 came. 3 Empty chair(s) left.
customer 9 came. 2 Empty chair(s) left.
customer 10 came. 1 Empty chair(s) left.
customer 11 came. 0 Empty chair(s) left.
customer 12 came and left.
customer 13 came and left.
customer 4 came and left.
customer 15 came and left.
customer 16 came and left.
customer 17 came and left.
customer 18 came and left.
customer 19 came and left.
cut hair 1 Empty chair(s) left.
customer 20 came. 0 Empty chair(s) left.
cut hair 1 Empty chair(s) left.
cut hair 2 Empty chair(s) left.
cut hair 3 Empty chair(s) left.
cut hair 4 Empty chair(s) left.
cut hair 5 Empty chair(s) left.
cut hair 6 Empty chair(s) left.
cut hair 7 Empty chair(s) left.
cut hair 8 Empty chair(s) left.
cut hair 9 Empty chair(s) left.
cut hair 10 Empty chair(s) left.

三个烟⻤问题

description

三个烟鬼问题实际上就是线程的并发问题:

  • 三个烟鬼,一个有烟草,一个有烟纸,一个有火柴
  • 上帝拿走两个人的材料给一个人,那么你那个人可以抽一支烟
  • 当那个人抽完这只烟的时候,上帝重新做决策

solution

#include <thread>
#include <atomic>
#include <iostream>
#include <chrono>
#include <random>

#define MAX_SMOKE_CNT 15
#define MAX_SMOKERS 3
using namespace std;

atomic<int> flag{0};
atomic<bool> if_exit{false};
thread smokers[MAX_SMOKERS];

void judge() {
random_device rd;
for (int i = 0; i < MAX_SMOKE_CNT; ++i) {
while (flag != 0) this_thread::yield();
int new_smoker = (int) ((rd() % 3) + 1);
flag = new_smoker;
}
if_exit = true;
}

void smoke(int id) {
while (true) {
while (flag != id) {
if (if_exit) return;
else this_thread::yield();
}
cout << "smoker " << id << " is smoking..." << endl;
this_thread::sleep_for(chrono::milliseconds(500));
flag = 0;
}
}

int main() {
for (int i = 0; i < MAX_SMOKERS; ++i) smokers[i] = thread(smoke, i + 1);
thread judger = thread(judge);
for (auto &smoker: smokers) { smoker.join(); }
judger.join();
}

优雅地使用了C++11中的随机数生成器。