leetcode多线程专题

  • Post author:
  • Post category:其他


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



版权声明:本文为weixin_42151929原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。