【C++】leetcode力扣——多线程系列代码及思路

一、semaphore.h

多线程编程里面需要用到的头文件:semaphore.h

同一个mutex,一般是在同一个线程进行加锁解锁的,如果mutex在a线程加锁,在b线程解锁,那么这就相当于把mutex当成信号量使用了,应该是属于未定义行为

int sem_post(sem_t *sem)

是以原子操作的方式给信号量的值加1(V操作),并发出信号唤醒等待线程 sem_wait

int sem_init(sem_t *sem, int pshared, unsigned int value)

pshared:控制信号量的类型,0表示线程间共享,其它表示进程间共享

value:信号量的初始值

参数 pshared > 0 时指定了 sem 处于共享内存区域,
所以可以在进程间共享该变量

int sem_wait(sem_t *sem)

以原子操作的方式给信号量的值减1(P操作),如果信号量的值为0函数将会等待,直到有线程增加了该信号量的值使其不再为0

二、leetcode相关题目

1114. 按序打印

考点:线程同步,设置两个同步信号量分别实现first和second、second和third方法之间的同步顺序

#include<semaphore.h>
class Foo {
    
    
public:
    Foo() {
    
    
        sem_init(&sem,0,0);
        sem_init(&sem1,0,0);
    }

    void first(function<void()> printFirst) {
    
    
        
        // printFirst() outputs "first". Do not change or remove this line.
        printFirst();
        sem_post(&sem);//通知调用wait sem的方法
    }

    void second(function<void()> printSecond) {
    
    
        sem_wait(&sem);
        
        // printSecond() outputs "second". Do not change or remove this line.
        printSecond();
        sem_post(&sem1);//通知调用wait sem1的方法

    }

    void third(function<void()> printThird) {
    
    
        sem_wait(&sem1);
        // printThird() outputs "third". Do not change or remove this line.
        printThird();
    }
    sem_t sem;//同步信号量
    sem_t sem1;
};

1115. 交替打印 FooBar

解法:双信号量实现线程同步
互斥信号量sem1作用范围是打印foo开始到打印完Bar结束,把这个范围内的两个单独过程看成一个整体过程,不可分割,从而保证了foo和bar为一个整体,使用sem同步信号量保证foo和bar的先后顺序

#include<semaphore.h>

class FooBar {
    
    
private:
    int n;
    sem_t sem;//同步信号量
    sem_t sem1;//互斥信号量

public:
    FooBar(int n) {
    
    
        this->n = n;
        sem_init(&sem1,0,1);
        sem_init(&sem,0,0);
    }

    void foo(function<void()> printFoo) {
    
    
        
        for (int i = 0; i < n; i++) {
    
    
            sem_wait(&sem1);
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            sem_post(&sem);
        }
    }

    void bar(function<void()> printBar) {
    
    
        
        for (int i = 0; i < n; i++) {
    
    
            sem_wait(&sem);
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            sem_post(&sem1);
         
        }
    }
};

后来发现只使用一个互斥信号量也可以通过,但这样做是存在问题的。比如有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法。如果先执行bar方法,sem1的值会变成2,后续结果就会出现问题

#include<semaphore.h>

class FooBar {
    
    
private:
    int n;
    sem_t sem1;//互斥信号量

public:
    FooBar(int n) {
    
    
        this->n = n;
        sem_init(&sem1,0,1);
    }

    void foo(function<void()> printFoo) {
    
    
        
        for (int i = 0; i < n; i++) {
    
    
            sem_wait(&sem1);
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            
        }
    }

    void bar(function<void()> printBar) {
    
    
        
        for (int i = 0; i < n; i++) {
    
    
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            sem_post(&sem1);
         
        }
    }
};

1226. 哲学家进餐

考点:多线程和死锁问题解决,怎么实现互斥

对于资源(筷子),应该认为这是五种临界资源,而不是一种临界资源。因为每位哲学家只能使用自己面前的两个筷子。对五只叉子进行资源编号0~4,并且定义第i位哲学家左侧的叉子编号为i,哲学家右侧的叉子编号为(i+1)%5(这里要注意对编号为0的进行特殊处理)

#include<semaphore.h>
class DiningPhilosophers {
    
    
public:
    DiningPhilosophers() {
    
    
        fork=vector<sem_t>(5);//5只叉子  注意这里初始化不能使用(5,0)会报错
        for(int i=0;i<5;i++){
    
    
           sem_init(&fork[i],0,1);//初始化叉子信号量,值为1
        }
        
        sem_init(&mutex,0,1);//初始化互斥信号量
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
    
    
        
        sem_wait(&mutex);//对拿叉子的过程加互斥
        sem_wait(&fork[philosopher]);//左边叉子加锁
        sem_wait(&fork[(philosopher+1)%5]);//右边叉子加锁
        pickLeftFork();//拿左边叉子
        pickRightFork();//拿右边叉子
        sem_post(&mutex);//拿完叉子后可以释放互斥信号量

        //吃饭
        eat();

        //吃完饭放下叉子
        putLeftFork();
        putRightFork();

        //叉子放下后解锁,唤醒sem_wait()函数,其他的哲学家可以进餐
        sem_post(&fork[philosopher]);//左边叉子解锁
        sem_post(&fork[(philosopher+1)%5]);//右边叉子解锁       
        
		
    }
    sem_t mutex;//互斥信号量
    vector<sem_t> fork;//叉子信号量
};

猜你喜欢

转载自blog.csdn.net/qq_43050258/article/details/130321869