C++11 標(biāo)準(zhǔn)庫(kù) std::thread 多線程使用教程

從 C++11 開(kāi)始,標(biāo)準(zhǔn)庫(kù)里已經(jīng)包含了對(duì)線程的支持,std::thread是C++11標(biāo)準(zhǔn)庫(kù)中的多線程的支持庫(kù),pthread.h 是標(biāo)準(zhǔn)庫(kù)沒(méi)有添加多線程之前的在Linux上用的多線程庫(kù)。std::thread 是面向?qū)ο蟮亩嗑€程庫(kù),使用簡(jiǎn)單,推薦在項(xiàng)目中使用 std::thread 代替 pthread.h。

修改 CMakeLists.txt

項(xiàng)目中用到了C++ 17的時(shí)間代碼風(fēng)格,需要修改為對(duì)應(yīng)的版本。

# CMakeLists.txt
set(CMAKE_CXX_STANDARD 17)

創(chuàng)建線程

#include <iostream>
#include <thread>

using namespace std;

void SayHello() {
    cout << "Hello World" << endl;
}

int main() {
    std::thread t1(SayHello);
    // 等待子線程結(jié)束才退出當(dāng)前線程
    pthread_exit(nullptr);
    return 0;
}

如果不加 pthread_exit(nullptr),會(huì)報(bào)libc++abi: terminating程序終止的錯(cuò)誤??梢酝ㄟ^(guò) detach() 函數(shù),將子線程和主線分離,子線程可以獨(dú)立繼續(xù)運(yùn)行,即使主線程結(jié)束,子線程也不會(huì)結(jié)束。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;

void SayHello() {
    cout << "Hello World" << endl;
}

int main() {
    std::thread t1(SayHello);
    t1.detach();
    this_thread::sleep_for(10ms);
    // 低于C++17使用這行代碼  this_thread::sleep_for(chrono::milliseconds(10));
    return 0;
}

傳遞參數(shù)

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;

void SayHello(int id, string name) {
    this_thread::sleep_for(10ms);
    cout << "ID:" << id << ", Hello " << name << endl;
}

int main() {
    std::thread t1(SayHello, 1, "Wiki");
    t1.join();
    return 0;
}

線程睡眠

using namespace std::literals::chrono_literals;
// 讓當(dāng)前線程睡眠 10 毫秒
this_thread::sleep_for(10ms);
// 低于C++17使用這行代碼  this_thread::sleep_for(chrono::milliseconds(10));
// 讓當(dāng)前線程睡眠 5 秒
this_thread::sleep_for(5s);

join() 等待線程運(yùn)行結(jié)束

join() 函數(shù)可以在當(dāng)前線程等待線程運(yùn)行結(jié)束。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;

void SayHello() {
    this_thread::sleep_for(10ms);
    cout << "Hello World" << endl;
}

int main() {
    std::thread t1(SayHello);
    t1.join();
    return 0;
}

condition_variable / wait / notify_one

使用 condition_variable 實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的實(shí)驗(yàn),通過(guò) wait 進(jìn)入線程等待,知道有其它的線程把當(dāng)前線程喚醒。

#include <iostream>
#include <thread>
#include <list>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;
condition_variable g_con;

list<int> products;

void test() {
    int product_id = 0;
    while (true) {
        products.push_back(++product_id);
        cout << "products 生產(chǎn): " << product_id << endl;
        std::unique_lock<std::mutex> lock(g_mutex);
        // 通知消費(fèi)者消費(fèi)
        g_con.notify_one();
        lock.unlock();
        if (product_id > 50) {
            break;
        }
        this_thread::sleep_for(2ms);
    }
}

int main() {
    std::thread t1(test);
    while (true) {
        std::unique_lock<std::mutex> lock(g_mutex);
        if (products.empty()) {
            cout << "沒(méi)有產(chǎn)品,等待" << endl;
            // 進(jìn)入等待,知道有新產(chǎn)品
            g_con.wait(lock);
        } else {
            int product_id = products.front();
            products.pop_front();
            cout << "消費(fèi)產(chǎn)品 " << product_id << endl;
            this_thread::sleep_for(2ms);
            if (product_id > 50) break;
        }
    }
    t1.join();
    return 0;
}

輸出結(jié)果:

沒(méi)有產(chǎn)品,等待
products 生產(chǎn): 1
消費(fèi)產(chǎn)品 1
products 生產(chǎn): 2
消費(fèi)產(chǎn)品 2
沒(méi)有產(chǎn)品,等待
沒(méi)有產(chǎn)品,等待
...

thread_local

C++11中提供了thread_local,thread_local定義的變量在每個(gè)線程都保存一份副本,而且互不干擾,在線程退出的時(shí)候自動(dòng)銷(xiāo)毀。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
thread_local int t_l_counter = 0;

void test() {
    cout << "flag1 t_l_counter: " << t_l_counter << endl;
    t_l_counter = 2;
}

int main() {
    t_l_counter = 1;
    std::thread t1(test);
    t1.join();
    cout << "flag2 t_l_counter: " << t_l_counter << endl;
    return 0;
}

結(jié)果:

flag1 t_l_counter: 0
flag2 t_l_counter: 1

同步鎖

#include <iostream>
#include <thread>
using namespace std;
void test() {
    cout << "task start thread ID: " << this_thread::get_id() << endl;
    this_thread::sleep_for(10ms);
    cout << "task end thread ID: " << this_thread::get_id() << endl;
}
int main() {
    std::thread t1(test);
    std::thread t2(test);
    std::thread t3(test);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

運(yùn)行結(jié)果:

task start thread ID: task start thread ID: task start thread ID: 0x70000fab00000x70000fb33000

0x70000fbb6000
task end thread ID: 0x70000fab0000
task end thread ID: task end thread ID: 0x70000fb33000
0x70000fbb6000

以上代碼數(shù)據(jù)的結(jié)果是無(wú)序的,如果我們需要同一時(shí)間只有一個(gè)線程在test函數(shù)中執(zhí)行代碼,那么就要加鎖,lock() 用于加鎖,而unlock() 解鎖。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;

void test() {
    g_mutex.lock();
    cout << "task start thread ID: " << this_thread::get_id() << endl;
    this_thread::sleep_for(10ms);
    cout << "task end thread ID: " << this_thread::get_id() << endl;
    g_mutex.unlock();
}
int main() {
    std::thread t1(test);
    std::thread t2(test);
    std::thread t3(test);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

運(yùn)行結(jié)果:

task start thread ID: 0x70000e4f2000
task end thread ID: 0x70000e4f2000
task start thread ID: 0x70000e46f000
task end thread ID: 0x70000e46f000
task start thread ID: 0x70000e3ec000
task end thread ID: 0x70000e3ec000

除了std::mutex(非遞歸的互斥量),還有std::timed_mutex(帶超時(shí)的非遞歸互斥量),std::recursive_mutex(遞歸互斥量)、std::recursive_timed_mutex(帶超時(shí)的遞歸互斥量)。

同步鎖封裝類(lèi)

可以創(chuàng)建一個(gè) ScopeMutex 類(lèi),通過(guò)構(gòu)造函數(shù)和析構(gòu)函數(shù)實(shí)現(xiàn)加鎖和解鎖,ScopeMutex 的作用域只在 {} 之內(nèi)加鎖。

#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;

class ScopeMutex {
public:
    explicit ScopeMutex(std::mutex &mutex) {
        this->mutex = &mutex;
        this->mutex->lock();
    }

    ~ScopeMutex() {
        this->mutex->unlock();
    }

    std::mutex *mutex;
};

void test() {
    cout << "task prepare thread ID: " << this_thread::get_id() << endl;
    {
        ScopeMutex scopeMutex(g_mutex);
        cout << "task start thread ID: " << this_thread::get_id() << endl;
        this_thread::sleep_for(10ms);
        cout << "task end thread ID: " << this_thread::get_id() << endl;
    }

}

int main() {
    std::thread t1(test);
    std::thread t2(test);
    std::thread t3(test);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

運(yùn)行結(jié)果:

task prepare thread ID: task prepare thread ID: 0x70000d9bd0000x70000d8b7000
task start thread ID: 0x70000d9bd000

task prepare thread ID: 0x70000d93a000
task end thread ID: 0x70000d9bd000
task start thread ID: 0x70000d8b7000
task end thread ID: 0x70000d8b7000
task start thread ID: 0x70000d93a000
task end thread ID: 0x70000d93a000
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容