C++ 多线程笔记(std::thread)

                     

贡献者: addis

   例程(编译时要给 linker 加上 -l pthread):

#include <thread>
#include <mutex>
#include <chrono>
using namespace std;

mutex mut;

// A dummy function
void myfun(int id, int *i)
{
    if (id == 1)
        this_thread::sleep_for(chrono::milliseconds(100));
    lock_guard<mutex> guard(mut);
    printf("id = %d, i = %d\n", id, *i);
    int j = *i+1;
    if (id == 2)
        this_thread::sleep_for(chrono::milliseconds(100));
    *i = j;
}

int main()
{
    int i = 0;
    thread th1(myfun, 1, &i);
    thread th2(myfun, 2, &i);
    myfun(0, &i);
    th1.join();
    th2.join();
    return 0;
}

   另外也可以把 mut 声明为 myfun() 中的一个 static 变量。

   另外注意 mutex 不光适用于 std::thread,对任何其他 threading 库例如 pthread 或者 OpenMP 都有效。

  

未完成:互斥锁、条件锁、读写锁以及自旋锁分别什么时候用?

thread_local 变量

1. std::condition_variable

   condition_variable 用于 block 一个或者多个线程(也就是让它睡觉,腾出算力做别的事情),直到某个另外的线程发送一个信号。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

// 把 condition_variable 需要 bool 变量一起封装起来更方便使用
class ConditionVar {
private:
    std::condition_variable cv;
    bool ready = false;

public:
    // 暂停一些线程
    void wait(std::mutex &mtx) {
        std::unique_lock<std::mutex> lock(mtx); // 上锁
        cv.wait(lock, [this]{ return ready; }); // 在线程休眠以前解锁,在唤醒时再次上锁
        // 注意如果在 wait 以前 ready=true,那么 wait 什么都不做。
        // 解锁
    }
    // 唤醒所有线程
    void notify_all(std::mutex &mtx) {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        cv.notify_all();
    }
    // 随机唤醒一个线程
    void notify_one(std::mutex &mtx) {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        cv.notify_one();
    }
};

std::mutex mtx;
ConditionVar cv;

void wait(int id) {
	printf("Thread %d started.\n", id);
    cv.wait(mtx); // 休眠
	printf("Thread %d is resumed.\n", id);
}

void resume() {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟一些工作
    cv.notify_all(mtx); // 全部唤醒
    printf("resume signal sent!\n");
}

int main() {
    std::thread threads[3];
    
    // 注意这里任何一个线程都可能先开启
    for (int i = 0; i < 2; ++i)
        threads[i] = std::thread(wait, i);

    threads[2] = std::thread(resume);

    for (auto &th : threads)
        th.join();
}
运行结果
Thread 0 started.
Thread 1 started.
resume signal sent!
Thread 0 is resumed.
Thread 1 is resumed.

   这有什么应用呢?比如说一个线程要准备一个全局的 string 变量给所有其他线程用。那么所有其他线程在使用该 string 之前就要 wait,而准备 string 的线程准备好之后就要 notify_all。

2. 自旋锁

   c++ 没有定义自旋锁,我们可以简单实现一个:

#include <atomic>
#include <thread>
#include <iostream>

class SpinLock {
private:
    std::atomic_flag lockFlag = ATOMIC_FLAG_INIT;

public:
    void lock() {
        while (lockFlag.test_and_set(std::memory_order_acquire)) {
            // Spin-wait (busy wait) until the lock is released
        }
    }

    void unlock() {
        lockFlag.clear(std::memory_order_release);
    }
};

void exampleFunction(SpinLock& spinLock) {
    spinLock.lock();
    std::cout << 123 << "234" << std::endl;
    spinLock.unlock();
}

int main() {
    SpinLock spinLock;
    std::thread t1(exampleFunction, std::ref(spinLock));
    std::thread t2(exampleFunction, std::ref(spinLock));

    t1.join();
    t2.join();

    return 0;
}


致读者: 小时百科一直以来坚持所有内容免费,这导致我们处于严重的亏损状态。 长此以往很可能会最终导致我们不得不选择大量广告以及内容付费等。 因此,我们请求广大读者热心打赏 ,使网站得以健康发展。 如果看到这条信息的每位读者能慷慨打赏 10 元,我们一个星期内就能脱离亏损, 并保证在接下来的一整年里向所有读者继续免费提供优质内容。 但遗憾的是只有不到 1% 的读者愿意捐款, 他们的付出帮助了 99% 的读者免费获取知识, 我们在此表示感谢。

                     

友情链接: 超理论坛 | ©小时科技 保留一切权利