C++ 多線程使用

線程

std::thread

創(chuàng)建線程

  1. 使用函數(shù)

    #include <thread>
    #include <iostream>
    
    void task(int i)
    {
        std::cout << i << std::endl;
    }
    
    int main()
    {
        for (int i = 0; i < 8; i++)
        {
            std::thread t(task, i);
            t.detach();
        }
        getchar();
        return 0;
    }
    
  2. 使用lambda表達(dá)式創(chuàng)建線程

    #include <thread>
    #include <iostream>
    
    int main()
    {
        for (int i = 0; i < 8; i++)
        {
            std::thread t([](int i){
                std::cout << i << std::endl;
            }, i);
            t.detach();
        }
        getchar();
        return 0;
    }
    
  3. 重載了()運(yùn)算符的類的實(shí)例

    #include <thread>
    #include <iostream>
    
    struct Task
    {
        void operator()(int i)
        {
            std::cout << i << std::endl;
        }
    };
    
    int main()
    {
        for (int i = 0; i < 8; i++)
        {
            Task task;
            std::thread t(task, i);
            t.detach();
        }
        getchar();
        return 0;
    }
    
  4. 在類中創(chuàng)建線程

    #include <iostream>
    #include <thread>
    #include <atomic>
    #include <chrono>
    
    class Worker {
    public:
        Worker() : stopFlag(false) {}
    
        // 啟動線程
        void start() {
            workerThread = std::thread(&Worker::run, this);
        }
    
        // 停止線程
        void stop() {
            stopFlag = true;
            if (workerThread.joinable()) {
                workerThread.join();
            }
        }
    
        // 析構(gòu)函數(shù)確保線程停止
        ~Worker() {
            stop();
        }
    
    private:
        std::thread workerThread;
        std::atomic<bool> stopFlag;
    
        // 線程運(yùn)行的函數(shù)
        void run() {
            while (!stopFlag) {
                std::cout << "線程正在運(yùn)行..." << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
            std::cout << "線程已停止" << std::endl;
        }
    };
    
    int main() {
        Worker worker;
    
        worker.start(); // 啟動線程
    
        std::this_thread::sleep_for(std::chrono::seconds(5)); // 主線程等待
    
        worker.stop(); // 停止線程
    
        return 0;
    }
    
    

參數(shù)傳遞

  1. 值傳遞

    #include <thread>
    #include <iostream>
    
    struct Task
    {
        void operator()(int i)
        {
            printf("thread &i : %p\n", &i);
        }
    };
    
    int main()
    {
        for (int i = 0; i < 1; i++)
        {
            printf("&i : %p\n", &i);
            Task task;
            std::thread t(task, i);
            t.detach();
        }
        getchar();
        return 0;
    }
    
  2. 引用傳遞
    引用傳遞的地址和主線程的地址也是不一致的

    #include <thread>
    #include <iostream>
    
    struct Task
    {
        void operator()(const int &i) // 這里必須是const,不然會報(bào)錯(cuò)
        {
            printf("thread &i : %p\n", &i);
        }
    };
    
    int main()
    {
        for (int i = 0; i < 1; i++)
        {
            printf("&i : %p\n", &i);
            Task task;
            std::thread t(task, i);
            t.detach();
        }
        getchar();
        return 0;
    }
    
  3. 指針傳遞

    #include <thread>
    #include <iostream>
    
    struct Task
    {
        void operator()(int *i)
        {
            printf("thread &i : %p\n", i);
        }
    };
    
    int main()
    {
        for (int i = 0; i < 1; i++)
        {
            printf("&i : %p\n", &i);
            Task task;
            std::thread t(task, &i);
            t.join(); // 這里使用join,確保i沒有被銷毀
        }
        getchar();
        return 0;
    }
    
  4. std::ref 傳參
    std::ref 可以保證子線程中的參數(shù)地址和主線程中的參數(shù)地址是一致的,即在子線程和主線程是同步的茶没。

    #include <thread>
    #include <iostream>
    
    struct Task
    {
        void operator()(int &i)
        {
            printf("thread &i : %p\n", &i);
        }
    };
    
    int main()
    {
        for (int i = 0; i < 1; i++)
        {
            printf("&i : %p\n", &i);
            Task task;
            std::thread t(task, std::ref(i));
            t.join();
        }
        getchar();
        return 0;
    }
    

std::mutex

  • 構(gòu)造函數(shù)
  • lock函數(shù) 調(diào)用線程將鎖住該互斥量土辩。
  • unlock函數(shù) 調(diào)用線程將釋放該互斥量松蒜。
  • try_lock函數(shù) 調(diào)用線程將嘗試鎖住該互斥量吼具,不成功返回false。
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
    #include <mutex>          // std::mutex
    
    volatile int counter(0); // non-atomic counter
    std::mutex mtx;           // locks access to counter
    
    void attempt_10k_increases() 
    {
        for (int i=0; i<10000; ++i) 
        {
            mtx.lock();
            ++counter;
            mtx.unlock();
        }
    }
    
    int main (int argc, const char* argv[]) {
        std::thread threads[10];
        for (int i=0; i<10; ++i)
            threads[i] = std::thread(attempt_10k_increases);
    
        for (auto& th : threads) th.join();
        std::cout << counter << " successful increases of the counter.\n";
    
        return 0;
    }
    

std::lock_guard

  • 自動鎖定和解鎖
    當(dāng) std::lock_guard 對象被創(chuàng)建時(shí)嫂便,它會自動鎖定提供的互斥鎖。當(dāng)該對象離開其作用域(即被銷毀)時(shí)狮含,它會自動解鎖互斥鎖顽悼。這種自動管理確保了即使在發(fā)生異常的情況下,互斥鎖也能被正確解鎖几迄。

  • 簡單易用
    std::lock_guard 的使用非常簡單蔚龙,只需要在需要保護(hù)的代碼塊之前創(chuàng)建它即可。無需顯式調(diào)用鎖定和解鎖函數(shù)映胁。

  • 非遞歸
    std::lock_guard 不支持遞歸鎖定木羹,即不能多次鎖定同一個(gè)互斥鎖。如果嘗試這樣做解孙,會導(dǎo)致未定義的行為坑填。

    #include <iostream>       // std::cout
    #include <thread>         // std::thread
    #include <mutex>          // std::mutex
    
    volatile int counter(0); // non-atomic counter
    std::mutex mtx;           // locks access to counter
    
    void attempt_10k_increases() 
    {
        for (int i=0; i<10000; ++i) 
        {
            std::lock_guard<std::mutex> l(mtx);
            ++counter;
        }
    }
    
    int main (int argc, const char* argv[]) {
        std::thread threads[10];
        for (int i=0; i<10; ++i)
            threads[i] = std::thread(attempt_10k_increases);
    
        for (auto& th : threads) th.join();
        std::cout << counter << " successful increases of the counter.\n";
    
        return 0;
    }
    

std::unique_lock

  • 可以在構(gòu)造時(shí)選擇是否鎖定互斥量。std::unique_lock<std::mutex> lock(my_mutex1, std::defer_lock); std::defer_lock的意思 就是 并沒有給mutex加鎖:初始化一個(gè)沒有加鎖的mutex弛姜,但是后面需要對unique_lock對象進(jìn)行加鎖
  • 支持手動鎖定和解鎖:可以在需要時(shí)調(diào)用 lock()unlock() 方法脐瑰。
  • 支持條件變量的等待:在等待條件變量時(shí),可以傳入 unique_lock廷臼,并在條件滿足時(shí)自動解鎖和重新鎖定苍在。
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
    #include <mutex>          // std::mutex
    
    volatile int counter(0); // non-atomic counter
    std::mutex mtx;           // locks access to counter
    
    void attempt_10k_increases() 
    {
        for (int i=0; i<10000; ++i) 
        {
            std::unique_lock<std::mutex> l(mtx);
            ++counter;
        }
    }
    
    int main (int argc, const char* argv[]) {
        std::thread threads[10];
        for (int i=0; i<10; ++i)
            threads[i] = std::thread(attempt_10k_increases);
    
        for (auto& th : threads) th.join();
        std::cout << counter << " successful increases of the counter.\n";
    
        return 0;
    }
    

條件變量

std::condition_variable

等待

條件變量的wait方法通常是通過以下方式實(shí)現(xiàn)的:

  1. 把線程放入等待隊(duì)列并釋放鎖绝页。
  2. 等待被顯式喚醒(notify_one 或 notify_all)或者因?yàn)樘摷賳拘驯徊僮飨到y(tǒng)強(qiáng)行喚醒。即使沒有執(zhí)行notify_one或者notify_all函數(shù)也有可能部分線程被虛假喚醒寂恬。
  3. 被喚醒后重新嘗試獲取鎖续誉。
void wait(std::unique_lock<std::mutex>& lock);

template <class Predicate>  
void wait (unique_lock<mutex>& lck, Predicate pred); // 解決虛假喚醒

喚醒

void notify_one() noexcept;
void notify_all() noexcept;

DEMO

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex


std::mutex mtx;
std::condition_variable cv;
bool ready = false;   // 全局標(biāo)志位

void printId(int id)
{
    std::unique_lock<std::mutex> lck(mtx);
    // 如果標(biāo)志位不為true,則等待
    while(!ready)
    {
        // 線程被阻塞初肉,直到標(biāo)志位變?yōu)閠rue酷鸦,此時(shí) mtx 被釋放,go 線程能夠獲取到鎖牙咏。
        cv.wait(lck);
    }
    // 另一種寫法, 等價(jià)于上面的寫法
    // cv.wait(lck, []{ return ready; }); 
    std::cout << "thread: " << std::this_thread::get_id() << " id: " << id << "\n";
}

void go()
{
    std::unique_lock<std::mutex> lck(mtx);
    // 改變?nèi)謽?biāo)志位
    ready = true;
    // 喚醒所有線程
    cv.notify_all();
}

int main()
{
    std::thread threads[10];

    for (int i = 0; i < 10; ++i)
    {
        threads[i] = std::thread(printId, i);
    }
    std::cout << "create done.\n" ;

    go();

    for (auto &t : threads)
    {
        t.join();
    }
    std::cout << "process done.\n" ;
    return 0;
}

數(shù)據(jù)傳遞

promise future

promise future

承諾的未來
std::promise的作用就是提供一個(gè)不同線程之間的數(shù)據(jù)同步機(jī)制臼隔,它可以存儲一個(gè)某種類型的值,并將其傳遞給對應(yīng)的future眠寿, 即使這個(gè)future不在同一個(gè)線程中也可以安全的訪問到這個(gè)值躬翁。

/** @file  20190815future.cpp
*  @note  
*  @brief
*  @author 
*  @date   2019-8-15
*  @note   
*  @history
*  @warning
*/
#include <iostream>
#include <functional>
#include <future>
#include <thread>
#include <chrono>
#include <cstdlib>

void thread_set_promise(std::promise<int>& promiseObj) {
    std::cout << "In a thread, making data...\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    promiseObj.set_value(35);
    std::cout << "Finished\n";
}

int main() {
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future();
    std::thread t(&thread_set_promise, std::ref(promiseObj));
    std::cout << futureObj.get() << std::endl;
    t.join();

    system("pause");
    return 0;
}

生產(chǎn)者消費(fèi)者

c++多線程實(shí)現(xiàn)生產(chǎn)者消費(fèi)者

線程池

c++11 線程池

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盯拱,隨后出現(xiàn)的幾起案子盒发,更是在濱河造成了極大的恐慌,老刑警劉巖狡逢,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宁舰,死亡現(xiàn)場離奇詭異,居然都是意外死亡奢浑,警方通過查閱死者的電腦和手機(jī)蛮艰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雀彼,“玉大人壤蚜,你說我怎么就攤上這事』惭疲” “怎么了袜刷?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長莺丑。 經(jīng)常有香客問我著蟹,道長,這世上最難降的妖魔是什么梢莽? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任萧豆,我火速辦了婚禮,結(jié)果婚禮上昏名,老公的妹妹穿的比我還像新娘涮雷。我一直安慰自己,他們只是感情好轻局,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布洪鸭。 她就那樣靜靜地躺著膜钓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卿嘲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天夫壁,我揣著相機(jī)與錄音拾枣,去河邊找鬼。 笑死盒让,一個(gè)胖子當(dāng)著我的面吹牛梅肤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邑茄,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姨蝴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肺缕?” 一聲冷哼從身側(cè)響起左医,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎同木,沒想到半個(gè)月后浮梢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡彤路,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年秕硝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洲尊。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡远豺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坞嘀,到底是詐尸還是另有隱情躯护,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布姆吭,位于F島的核電站榛做,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏内狸。R本人自食惡果不足惜检眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昆淡。 院中可真熱鬧锰瘸,春花似錦、人聲如沸昂灵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至管削,卻和暖如春倒脓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背含思。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工崎弃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人含潘。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓饲做,卻偏偏與公主長得像,于是被迫代替她去往敵國和親遏弱。 傳聞我的和親對象是個(gè)殘疾皇子盆均,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容