leetcode多线程专题

    科技2022-08-18  103

    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()

     

     

     

     

     

    Processed: 0.021, SQL: 9