多线程 C++

C++ unique_lock future condition_variable

Posted on 2021-02-27,5 min read

多线程的一些基础语法。

std::unique_lock

使用std::unique_lock 比std::lock_guard更加灵活:std::lock_guard 不能显式的调用 lock 和 unlock,而 std::unique_lock 可以在声明后的任意位 置调用,可以缩小锁的作用范围,提供更高的并发度。

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

using namespace std;
int v = 1;
void critical_sectin(int change_v){
    static mutex mtx;
    std::this_thread::sleep_for(std::chrono::milliseconds(change_v));
    std::unique_lock<std::mutex> lock(mtx);
    v = change_v;
    std::cout << v << std::endl;

    lock.unlock();

    change_v++;

    lock.lock();
    v = change_v;
    std::cout << v << std::endl;

}
int main(){
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    std::thread t1(critical_sectin, 590);
    std::thread t2(critical_sectin, 66);
    t1.join();
    t2.join();
    return 0;
}

std::future 对线程的返回结果进行封装

#include <iostream>
#include <mutex>
#include <thread>
#include <future>
using namespace std;

int main(){
    std::packaged_task<int(int)> task([](int a) -> int{
        std::cout << "inside packaged_task " << a << std::endl;
        return a;
    });
    std::future<int> result = task.get_future();

    std::thread(std::move(task), 8).detach(); //创建thread,传入package_task的参数
    
    result.wait();
    std::cout << result.get() << std::endl;

    
    return 0;
}

std::condition_variable

用于同步,解决死锁

#include <iostream>
#include <mutex>
#include <thread>
#include <future>
#include <condition_variable>
#include <queue>
#include <string>
using namespace std;

int main(){
    std::condition_variable cv;
    std::mutex mtx;
    std::queue<int> produced_nums;
    bool notify = false;
    auto producer = [&](){
        for (int i=1; ; i++){
            std::this_thread::sleep_for(std::chrono::milliseconds(900));
            std::unique_lock<mutex> lock(mtx);

            std::cout << "producing " << i << std::endl;

            produced_nums.push(i);
            notify = true;
            cv.notify_all();

        }
    };
    auto consumer = [&](int csid){
        while (true){
            unique_lock<mutex> lock(mtx);

            while (!notify){
                cv.wait(lock);
            }

            std::this_thread::sleep_for(std::chrono::milliseconds(1000));

            std::cout << csid << " lock success" << std::endl;
            if (!produced_nums.empty()){
                auto result = produced_nums.front();
                produced_nums.pop();
                std::cout << "consumer " << csid << " : " << result << " " << produced_nums.size() << std::endl;
            }
            notify = false;

        }
    };

    thread produce(producer);
    vector<thread> pool;
    for (int i = 0; i < 10; ++i) {
        pool.push_back(thread(consumer, i));
    }

    produce.join();
    for (int i = 0; i < 10; ++i) {
        pool[i].join();
    }

    return 0;
}

condition_variable需要mutex和condition两个变量,在cv.wait(lock)后,需要检查条件是否满足(因为存在假唤醒)。

consumer中wait被signal之后的过程:

首先要拿到lock,然后cv.wait(lock, {return is_ready;});之后,线程开始休眠,被加入一个唤醒队列,同时释放锁。
cv.notify_all()之后,所有在wait的consumer线程被唤醒,然后开始lock,注意,如果mutex此时是被locked的,这些线程也不会再休眠了,会一直尝试加锁,直到加锁成功,进入临界区。

官方样例:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
 
void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});
 
    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';
 
    worker.join();
}

output:

main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing

下一篇: C++ T&&推导T的类型→