新聞中心
最近在用c++搞項目,因為多線程要做一個類似cnt的保護,今天學習了c++的原子操作。

成都創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為烏拉特后企業(yè)提供專業(yè)的網(wǎng)站設計、成都網(wǎng)站建設,烏拉特后網(wǎng)站改版等技術服務。擁有十年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
探索c++的原子類型
std::atomic 類型是 C++ 提供的一種機制,用于實現(xiàn)多線程之間的安全共享數(shù)據(jù)。它通過原子操作來確保對共享變量的操作是不可分割的。在多線程環(huán)境下,如果沒有適當?shù)耐綑C制,對共享變量的讀寫可能會導致競爭條件,進而引發(fā)不確定的行為。std::atomic 類型提供了一種解決方案,讓我們能夠以線程安全的方式訪問這些變量。
關于具體的函數(shù)和詳細介紹可以訪問這里:https://cplusplus.com/reference/atomic/atomic/?kw=atomic
這里介紹幾個常用的:
- load 和 store:用于讀取和寫入原子變量的值。
- exchange:交換原子變量的值,并返回之前的值。
- compare_exchange_strong 和 compare_exchange_weak:比較并交換操作,可在特定條件下修改原子變量的值。
- fetch_add 和 fetch_sub:原子地執(zhí)行加法和減法操作,并返回之前的值。
這里原子操作后為什么要返回之前的值呢?
以fetch_add為例,fetch_add是用于對原子變量進行原子性地增加操作。它執(zhí)行一個原子的加法操作,并返回加法操作之前的原子變量的值。
這種設計是基于并發(fā)編程中的常見需求。返回之前的值允許程序員在執(zhí)行加法操作后,獲取加法之前的原始值。這樣做有以下幾個方面的優(yōu)點:
- 原子性操作的完整性:在多線程并發(fā)環(huán)境下,如果需要進行原子性的加法操作,同時又需要獲取加法前的值,fetch_add 的設計能夠保證這兩個操作的原子性。它在單個原子操作中完成增加操作,并返回增加前的值,避免了在多線程環(huán)境下的競態(tài)條件。
- 避免競態(tài)條件:返回之前的值可以讓程序員在進行加法操作之后,檢查原子變量的舊值,并根據(jù)舊值進行后續(xù)的操作。這對于實現(xiàn)一些特定的同步模式或算法是非常有用的,因為它避免了因為操作間的競爭導致的意外結果。
舉個栗子
這里做一個簡單的線程池,并實現(xiàn)一個task,task的任務就是對原子變量counter進行遞增,最后我們看結果是否與預期一致,這里線程池實現(xiàn)10個線程,給線程池推送100000個task。
#include
#include
#include
#include
#include
#include
#include
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 task;
{
std::unique_lock lock(queueMutex);
condition.wait(lock, [this] { return stop || !tasks.empty(); });
if (stop && tasks.empty()) {
return;
}
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
template
void AddTask(F&& f) {
{
std::lock_guard lock(queueMutex);
tasks.emplace(std::forward(f));
}
condition.notify_one();
}
~ThreadPool() {
{
std::lock_guard lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : threads) {
worker.join();
}
}
private:
std::vector threads;
std::queue> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop;
};
int main() {
std::atomic 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;
//注意:這里不會確保所有任務已經(jīng)執(zhí)行完畢,僅僅是等待一段時間以展示結果
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "Final Counter Value: " << counter << std::endl;
return 0;
} 我們預期最后的結果是100000。g++編譯,不要忘記加-lpthread,執(zhí)行:
細心的小伙伴可能發(fā)現(xiàn)我的代碼直接使用的counter++,這里需要注意,這只是個簡單的測試代碼,實際項目中要最好使用counter.fetch_add(1),因為counter++不保證++是個原子操作。我在項目中遇到了該問題,最后加出來總會比預期值少,后來換成fetch_add后就正常了。
本文標題:解鎖C++并發(fā)編程的鑰匙:探索Atomic變量
本文路徑:http://www.5511xx.com/article/djiophi.html


咨詢
建站咨詢
