C++11中的原子操作(atomic operation)


C++11中的原子操作(atomic operation)

所謂的原子操作,取的就是“原子是最小的、不可分割的最小個體”的意義,它表示在多個線程訪問同一個全局資源的時候,能夠確保所有其他的線程都不在同一時間內訪問相同的資源。也就是他確保了在同一時刻只有唯一的線程對這個資源進行訪問。這有點類似互斥對象對共享資源的訪問的保護,但是原子操作更加接近底層,因而效率更高。

在以往的C++標准中並沒有對原子操作進行規定,我們往往是使用匯編語言,或者是借助第三方的線程庫,例如intel的pthread來實現。在新標准C++11,引入了原子操作的概念,並通過這個新的頭文件提供了多種原子操作數據類型,例如,atomic_bool,atomic_int等等,如果我們在多個線程中對這些類型的共享資源進行操作,編譯器將保證這些操作都是原子性的,也就是說,確保任意時刻只有一個線程對這個資源進行訪問,編譯器將保證,多個線程訪問這個共享資源的正確性。從而避免了鎖的使用,提高了效率。

我們還是來看一個實際的例子。假若我們要設計一個廣告點擊統計程序,在服務器程序中,使用多個線程模擬多個用戶對廣告的點擊:

#include <boost/thread/thread.hpp>
#include <atomic> 
#include <iostream>
#include <time.h>

using namespace std;
// 全局的結果數據 
long total = 0; 

// 點擊函數
void click()
{
    for(int i=0; i<1000000;++i)
    {
        // 對全局數據進行無鎖訪問 
        total ++;     
    }
}
 
 
int main(int argc, char* argv[])
{
    // 計時開始
    clock_t start = clock();
    // 創建100個線程模擬點擊統計
    boost::thread_group threads;
    for(int i=0; i<100;++i) 
    {
        threads.create_thread(click);
    }

    threads.join_all();
    // 計時結束
    clock_t finish = clock();
    // 輸出結果
    cout<<"result:"<<total<<endl;
    cout<<"duration:"<<finish -start<<"ms"<<endl;
    return 0;
}

從執行的結果來看,這樣的方法雖然非常快,但是結果不正確
E:\SourceCode\MinGW>thread.exe
result:87228026
duration:528ms

很自然地,我們會想到使用互斥對象來對全局共享資源的訪問進行保護,於是有了下面的實現:

long total = 0;
// 對共享資源進行保護的互斥對象
mutex m;

void click()
{
    for(int i=0; i<1000000;++i)
    {
        // 訪問之前,鎖定互斥對象
        m.lock();
        total++;
        // 訪問完成后,釋放互斥對象 
        m.unlock();
    }
}
互斥對象的使用,保證了同一時刻只有唯一的一個線程對這個共享進行訪問,從執行的結果來看,互斥對象保證了結果的正確性,但是也有非常大的性能損失,從剛才的528ms變成了現在的8431,用了原來時間的10多倍的時間。這個損失夠大。
E:\SourceCode\MinGW>thread.exe
result:100000000
duration:8431ms

如果是在C++11之前,我們的解決方案也就到此為止了,但是,C++對性能的追求是永無止境的,他總是想盡一切辦法榨干CPU的性能。在C++11中,實現了原子操作的數據類型(atomic_bool,atomic_int,atomic_long等等),對於這些原子數據類型的共享資源的訪問,無需借助mutex等鎖機制,也能夠實現對共享資源的正確訪問。

// 引入原子數據類型的頭文件
#include <atomic> 
 

// 用原子數據類型作為共享資源的數據類型
atomic_long total(0);
//long total = 0;
 
void click()
{
    for(int i=0; i<1000000;++i)
    {
        // 僅僅是數據類型的不同而以,對其的訪問形式與普通數據類型的資源並無區別
        total ++;
    }
}

我們來看看使用原子數據類型之后的效果如何:
E:\SourceCode\MinGW>thread.exe
result:100000000
duration:2105ms

結果正確!耗時只是使用mutex互斥對象的四分之一!也僅僅是不采用任何保護機制的時間的4倍。可以說這是一個非常不錯的成績了。

原子操作的實現跟普通數據類型類似,但是它能夠在保證結果正確的前提下,提供比mutex等鎖機制更好的性能,如果我們要訪問的共享資源可以用原子數據類型表示,那么在多線程程序中使用這種新的等價數據類型,是一個不錯的選擇。

i++和++i是否為原子操作

一.i++

i++的操作分三步:

(1)棧中取出i

(2)i自增1

(3)將i存到棧

所以i++不是原子操作,上面的三個步驟中任何一個步驟同時操作,都可能導致i的值不正確自增

二.++i

在多核的機器上,cpu在讀取內存i時也會可能發生同時讀取到同一值,這就導致兩次自增,實際只增加了一次。

 

綜上,我認為i++和++i都不是原子操作。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM