最近在用c++搞項(xiàng)目,因?yàn)槎嗑€程要做一個(gè)類似cnt的保護(hù),今天學(xué)習(xí)了c++的原子操作。
std::atomic 類型是 C++ 提供的一種機(jī)制,用于實(shí)現(xiàn)多線程之間的安全共享數(shù)據(jù)。它通過(guò)原子操作來(lái)確保對(duì)共享變量的操作是不可分割的。在多線程環(huán)境下,如果沒(méi)有適當(dāng)?shù)耐綑C(jī)制,對(duì)共享變量的讀寫可能會(huì)導(dǎo)致競(jìng)爭(zhēng)條件,進(jìn)而引發(fā)不確定的行為。std::atomic 類型提供了一種解決方案,讓我們能夠以線程安全的方式訪問(wèn)這些變量。
關(guān)于具體的函數(shù)和詳細(xì)介紹可以訪問(wèn)這里:https://cplusplus.com/reference/atomic/atomic/?kw=atomic
這里介紹幾個(gè)常用的:
這里原子操作后為什么要返回之前的值呢?
以fetch_add為例,fetch_add是用于對(duì)原子變量進(jìn)行原子性地增加操作。它執(zhí)行一個(gè)原子的加法操作,并返回加法操作之前的原子變量的值。
這種設(shè)計(jì)是基于并發(fā)編程中的常見(jiàn)需求。返回之前的值允許程序員在執(zhí)行加法操作后,獲取加法之前的原始值。這樣做有以下幾個(gè)方面的優(yōu)點(diǎn):
這里做一個(gè)簡(jiǎn)單的線程池,并實(shí)現(xiàn)一個(gè)task,task的任務(wù)就是對(duì)原子變量counter進(jìn)行遞增,最后我們看結(jié)果是否與預(yù)期一致,這里線程池實(shí)現(xiàn)10個(gè)線程,給線程池推送100000個(gè)task。
#include <iostream>#include <thread>#include <mutex>#include <condition_variable>#include <queue>#include <functional>#include <atomic>class ThreadPool {public: ThreadPool(size_t numThreads) : stop(false) { for (size_t i = 0; i < numThreads; ++i) { threads.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(queueMutex); condition.wait(lock, [this] { return stop || !tasks.empty(); }); if (stop && tasks.empty()) { return; } task = std::move(tasks.front()); tasks.pop(); } task(); } }); } } template <class F> void AddTask(F&& f) { { std::lock_guard<std::mutex> lock(queueMutex); tasks.emplace(std::forward<F>(f)); } condition.notify_one(); } ~ThreadPool() { { std::lock_guard<std::mutex> lock(queueMutex); stop = true; } condition.notify_all(); for (std::thread& worker : threads) { worker.join(); } }private: std::vector<std::thread> threads; std::queue<std::function<void()>> tasks; std::mutex queueMutex; std::condition_variable condition; bool stop;};int main() { std::atomic<int> counter(0); ThreadPool pool(10); constexpr int numTasks = 100000; for (int i = 0; i < numTasks; ++i) { pool.AddTask([&counter]() { counter++; }); } std::cout << "Waiting for tasks to complete..." << std::endl; //注意:這里不會(huì)確保所有任務(wù)已經(jīng)執(zhí)行完畢,僅僅是等待一段時(shí)間以展示結(jié)果 std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "Final Counter Value: " << counter << std::endl; return 0;}
我們預(yù)期最后的結(jié)果是100000。g++編譯,不要忘記加-lpthread,執(zhí)行:
細(xì)心的小伙伴可能發(fā)現(xiàn)我的代碼直接使用的counter++,這里需要注意,這只是個(gè)簡(jiǎn)單的測(cè)試代碼,實(shí)際項(xiàng)目中要最好使用counter.fetch_add(1),因?yàn)閏ounter++不保證++是個(gè)原子操作。我在項(xiàng)目中遇到了該問(wèn)題,最后加出來(lái)總會(huì)比預(yù)期值少,后來(lái)?yè)Q成fetch_add后就正常了。
本文鏈接:http://www.tebozhan.com/showinfo-26-37660-0.html解鎖 C++ 并發(fā)編程的鑰匙:探索 Atomic 變量
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: 一圖詳解五種前端架構(gòu)
下一篇: 深入淺出JavaScript異步編程