LeetCode-1114-按序打印

文章介绍了C++中用于多线程同步的关键机制,包括互斥锁(使用lock_guard和unique_lock实现RAII),条件变量用于线程间的同步通信,以及信号量和原子操作在处理资源竞争问题上的应用。强调了正确管理和使用这些工具的重要性,以防止数据争用和未定义行为。
摘要由CSDN通过智能技术生成

在这里插入图片描述

1、互斥锁

根据 C++ 标准,在一个线程尝试对一个 mutex 对象进行 unlock 操作时,mutex 对象的所有权必须在这个线程上;也就是说,应该 由同一个线程来对一个 mutex 对象进行 lock 和 unlock 操作,否则会产生未定义行为。

基于以上背景,因为 mutex 对象本身是不保护任何数据的,我们只是通过 mutex 的机制来保护数据被同时访问,所以最好 使用 lock_guard 或者 unique_lock 提供的 RAII 机制来管理 mutex 对象,而不是直接操作 mutex 对象;其中 lock_guard 只拥有构造和析构函数,用来实现 RAII 机制,而 unique_lock 是一个完整的 mutex 所有权包装器,封装了所有 mutex 的函数。

RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取就是初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法。利用的就是C++构造的对象最终会被销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。

这两种锁都可以对std::mutex进行封装,实现RAII的效果。绝大多数情况下这两种锁是可以互相替代的,区别是unique_lock比lock_guard能提供更多的功能特性(但需要付出性能的一些代价),如下:
1、unique_lock可以实现延时锁,即先生成unique_lock对象,然后在有需要的地方调用lock函数,lock_guard在对象创建时就自动进行lock操作了;
2、unique_lock可以在需要的地方调用unlock操作,而lock_guard只能在其对象生命周期结束后自动Unlock;
正是由于这两个差异特性,unique_lock可以用于一次性锁多个锁以及用于条件变量的搭配使用,而lock_guard做不到。

class Foo {
    mutex mtx_1, mtx_2;
    unique_lock<mutex> lock_1, lock_2;
public:
    Foo() : lock_1(mtx_1, try_to_lock), lock_2(mtx_2, try_to_lock) {
    }

    void first(function<void()> printFirst) {
        printFirst();
        lock_1.unlock();
    }

    void second(function<void()> printSecond) {
        lock_guard<mutex> guard(mtx_1);
        printSecond();
        lock_2.unlock();
    }

    void third(function<void()> printThird) {
        lock_guard<mutex> guard(mtx_2);
        printThird();
    }
};

2、条件变量

利用条件变量cv来控制修改特定变量k,进而去通知条件变量,唤醒下一个函数继续执行。


`std::condition_variable` 是一种用来同时阻塞多个线程的**同步原语**(synchronization primitive),**`std::condition_variable` 必须和 `std::unique_lock` 搭配使用**class Foo {
    condition_variable cv;
    mutex mtx;
    int k = 0;
public:
    void first(function<void()> printFirst) {
        printFirst();
        k = 1;
        cv.notify_all();    // 通知其他所有在等待唤醒队列中的线程
    }

    void second(function<void()> printSecond) {
        unique_lock<mutex> lock(mtx);   // lock mtx
        cv.wait(lock, [this](){ return k == 1; });  // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 1 才能继续运行
        printSecond();
        k = 2;
        cv.notify_one();    // 随机通知一个(unspecified)在等待唤醒队列中的线程
    }

    void third(function<void()> printThird) {
        unique_lock<mutex> lock(mtx);   // lock mtx
        cv.wait(lock, [this](){ return k == 2; });  // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 2 才能继续运行
        printThird();
    }
};

template<typename _Predicate>
void wait(unique_lock<mutex>& __lock, _Predicate __p)
{
    while (!__p())
        wait(__lock);
}

3、信号量

C++ 标准库中并没有信号量的实现和封装,我们可以用 C 语言提供的 <sempahore.h> 库来解决。

#include <semaphore.h>

class Foo {
private:
    sem_t sem_1, sem_2;

public:
    Foo() {
        sem_init(&sem_1, 0, 0), sem_init(&sem_2, 0, 0);
    }

    void first(function<void()> printFirst) {
        printFirst();
        sem_post(&sem_1);
    }

    void second(function<void()> printSecond) {
        sem_wait(&sem_1);
        printSecond();
        sem_post(&sem_2);
    }

    void third(function<void()> printThird) {
        sem_wait(&sem_2);
        printThird();
    }
};

4、原子操作

我们平时进行的数据修改都是非原子操作,如果多个线程同时以非原子操作的方式修改同一个对象可能会发生数据争用,从而导致未定义行为;而 原子操作能够保证多个线程顺序访问,不会导致数据争用,其执行时没有任何其它线程能够修改相同的原子对象。

class Foo {
    std::atomic<bool> a{ false };
    std::atomic<bool> b{ false };
public:
    void first(function<void()> printFirst) {
        printFirst();
        a = true;
    }

    void second(function<void()> printSecond) {
        while (!a)
            this_thread::sleep_for(chrono::milliseconds(1));//休眠一毫秒
        printSecond();
        b = true;
    }

    void third(function<void()> printThird) {
        while (!b)
            this_thread::sleep_for(chrono::milliseconds(1));
        printThird();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值