1.经典哲学家进餐问题
参考:
https://blog.csdn.net/Yun_Ge/article/details/89177918
避免死锁共有三种策略:(wait和signal是线程的函数,lock和unlock是mutex的函数,wait和signal函数中可以使用condition_variable类型的变量,该类型变量有wait(lock)和notify_one()函数(库函数))
- 保证同时饥饿的哲学家数量小于5,总之就是所有的哲学家不能同时进餐。(定义信号量count,保证至少有一个人可以正常进餐)
#include<iostream>
using namespace std;
class Semaphore{
public:
Semaphore(int count = 0):count_(count){}
void Set(int count){
count_ = count;
}
void signal(){
unique_lock<mutex> lock(mutex_);
++count_;
cv_.notify_one();
}
void wait(){
unique_lock<mutex> lock(mutex_);
while(count_ <= 0){
cv_.wait(lock);
}
--count_;
}
private:
mutex mutex_;
condition_variable cv_;
int count_;
};
class DiningPhilosophers {
public:
DiningPhilosophers() {
guid.Set(4);
}
void wantsToEat(int philosopher,
function<void()> pickLeftFork,
function<void()> pickRightFork,
function<void()> eat,
function<void()> putLeftFork,
function<void()> putRightFork) {
int l = philosopher;
int r = (philosopher+1)%5;
guid.wait();
lock[l].lock();
lock[r].lock();
pickLeftFork();
pickRightFork();
eat();
putRightFork();
putLeftFork();
lock[r].unlock();
lock[l].unlock();
guid.signal();
}
private:
mutex lock[5];
Semaphore guid;
};
- 保证只有同时拿到两只筷子才可以进餐。(AND型信号量或者利用信号量保护机制(记录型信号量))
#include<iostream>
using namespace std;
class DiningPhilosophers {
public:
DiningPhilosophers(){
}
void wantsToEat(int philosopher,
function<void()> pickLeftFork,
function<void()> pickRightFork,
function<void()> eat,
function<void()> putLeftFork,
function<void()> putRightFork) {
int l = philosopher;
int r = (philosopher+1) % 5;
mu_.lock();
lock[l].lock();
lock[r].lock();
pickLeftFork();
pickRightFork();
mu_.unlock();
eat();
putRightFork();
putLeftFork();
lock[r].unlock();
lock[l].unlock();
}
private:
mutex lock[5];
mutex mu_;
};
- 规定奇数号的人先拿起他左边的筷子,偶数号的人先拿起他右边的筷子。最后总会有一个人能获得两只筷子。
class DiningPhilosophers {
public:
DiningPhilosophers(){
}
void wantsToEat(int philosopher,
function<void()> pickLeftFork,
function<void()> pickRightFork,
function<void()> eat,
function<void()> putLeftFork,
function<void()> putRightFork) {
int l = philosopher;
int r = (philosopher+1) % 5;
if(l%2 == 1){
lock[l].lock();
lock[r].lock();
}
else{
lock[r].lock();
lock[l].lock();
}
pickRightFork();
pickLeftFork();
eat();
putLeftFork();
putRightFork();
lock[l].unlock();
lock[r].unlock();
}
private:
mutex lock[5];
};
2.按序打印
- mutex加锁解锁
class Foo {
public:
Foo() {
m2.lock();
m3.lock();
}
void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
m2.unlock();
}
void second(function<void()> printSecond) {
m2.lock();
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
m3.unlock();
}
void third(function<void()> printThird) {
m3.lock();
// printThird() outputs "third". Do not change or remove this line.
printThird();
m3.unlock();
}
private:
mutex m2,m3;
};
- 使用条件变量
1116.打印零与奇偶数
方法一:信号量需要注意的点:
- sem_t是类型
- 使用需要包含头文件<semaphore.h>
- sem_init三个参数,第一个指向信号量对象的指针,第二个是指明信号量类型,不为0表示该信号量进程间共享,否则表示当前进程的所有线程共享。初始化一个匿名信号量
- sem_post是给信号量的值加1,是个原子操作,sem_wait是原子操作,减一
- sem_destroy销毁一个匿名信号量
#include<semaphore.h>
class ZeroEvenOdd {
private:
int n;
sem_t zero_sem;
sem_t even_sem;
sem_t odd_sem;
public:
ZeroEvenOdd(int n) {
this->n = n;
sem_init(&zero_sem,0,1);
sem_init(&even_sem,0,0);
sem_init(&odd_sem,0,0);
}
// printNumber(x) outputs "x", where x is an integer.
void zero(function<void(int)> printNumber) {
for(int i = 1;i <= n;++i){
sem_wait(&zero_sem);
printNumber(0);
if(i & 1) sem_post(&odd_sem);
else sem_post(&even_sem);
}
}
void even(function<void(int)> printNumber) {
for(int i = 2;i <= n;i+=2){
sem_wait(&even_sem);
printNumber(i);
sem_post(&zero_sem);
}
}
void odd(function<void(int)> printNumber) {
for(int i = 1;i<=n;i+=2){
sem_wait(&odd_sem);
printNumber(i);
sem_post(&zero_sem);
}
}
};
方法二:互斥锁
1115.交替打印FooBar(见leetcode题解)
注意事项:
- pthread_mutex_t 是互斥锁类型
- pthread_mutex_init 互斥锁初始化函数(创建锁),有两个参数,第一个指向锁对象的指针,第二个表示互斥锁的属性
* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是
普通锁
。
当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁
。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,
嵌套锁
,
允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁
。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,
检错锁
,如果
同一个线程请求同一个锁,则返回EDEADLK
,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,
适应锁
,动作最简单的锁类型,仅
等待解锁后重新竞争
。
- pthread_mutex_lock()
- pthread_mutex_unlock()