1. 程式人生 > >C++中atomic 資料型別操作

C++中atomic 資料型別操作

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

在以往的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 += 1;     
    }
}
 
 
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 += 1;
        // 訪問完成後,釋放互斥物件 
        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 += 1;
    }
}

我們來看看使用原子資料型別之後的效果如何:
E:\SourceCode\MinGW>thread.exe
result:100000000
duration:2105ms

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

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

相關推薦

C++atomic 資料型別操作

所謂的原子操作,取的就是“原子是最小的、不可分割的最小個體”的意義,它表示在多個執行緒訪問同一個全域性資源的時候,能夠確保所有其他的執行緒都不在同一時間內訪問相同的資源。也就是他確保了在同一時刻只有唯一的執行緒對這個資源進行訪問。這有點類似互斥物件對共享資源的訪

Object-C 資料型別轉換 NSData轉NSString,Byte,UIImage

1,NSData 與 NSString   NSData --> NSString   NSString *aString = [[NSString alloc] initWithData:adata encoding:NSUTF8StringEncoding];   NSStri

C/C++各種 資料型別、結構體、類 佔用位元組數分析與總結

一、基本資料型別在不同編譯器下佔用位元組數比較與總結,測試過程不詳述了,直接看下錶結論! 下表中右側總結部分是依據佔用位元組數進行著色,同一種顏色型別的資料成員佔用位元組數要麼一致,要麼具有同樣的性質,這樣比較容易理解的記憶。 佔用位元組數

C++輸入資料型別判斷,輸入型別錯誤後,提示使用者重新輸入直至其輸入正確

要求輸入number,但是使用者鍵入了字母A,仍然有結果,但是不正確,同時後面的程式碼自行運行了,沒有辦法去輸入string了。所以要改進,可以判斷輸入的是否為正確的資料型別 利用cin.good()和cin.fail()判斷: cin.good()為true時,輸入的資料型別與定

C++基本資料型別位元組數及取值範圍【轉】

【轉自】:http://blog.csdn.net/a775992553/article/details/8790241 機器字長:是指計算機進行一次整數運算所能處理的二進位制資料的位數(整數運算即定點整數運算)。機器字長也就是運算器進行定點數運算的字長,通常也是CPU內部資料通路的寬度。現在一

詳解C++基本資料型別位元組數

C標準中並沒有具體規定哪個基本型別應該是多少位元組數,但有幾條鐵定的原則(ANSI/ISO制訂的): sizeof(short int)<=sizeof(int) sizeof(int)<=sizeof(long int) short

Object-c 資料型別

導航: 基本型別 ID 物件型別常見的有 物件型別 -NSLog -NSNumber -NSString和NSMutableString -NSArray和NSMutableArray -NSSet和NSMutableSet -NSDictionar

學習筆記(C++基礎資料型別在記憶體的表現形式)

一、整數型別 C++提供的整數型別有三種:int long short ,在Microsoft Visual C++ 6.0中,int型別與long型別在記憶體中都佔4個位元組,short型別佔兩個位元組。 在C++中整數型別又可以分為有符號與無符號型別兩種。 無符號整

C/C++基本資料型別在不同系統所佔空間大小

關於這個基本的問題,很早以前就很清楚了,C標準中並沒有具體給出規定那個基本型別應該是多少位元組數,而且這個也與機器、OS、編譯器有關,比如同樣是在32bits的作業系統系,VC++的編譯器下int型別

C++資料型別轉換方法總結

int到char*,或者反過來從char*到int,在C/C++中到底有多少種轉換方法呢?符合標準的大概有四種。即C資料轉換函式族、sprintf/snprintf/sscanf函式族、字串流std::stringstream、std::strsteam。不符合標準卻又廣為使用的包括CString和boost

C++資料型別轉換方法

摘要:本文總結了C/C++中的多種資料型別轉換方法,並比較了各自的優劣。給出了推薦的使用建議。 從int到char*,或者反過來從char*到int,在C/C++中到底有多少種轉換方法呢?符合標準的大概有四種。即C資料轉換函式族、sprintf/snprintf/sscanf函式族、字串流std::stri

C#實體類資料型別後面新增問號是什麼意思

C#實體類中在資料型別後面新增問號是什麼意思  public static DateTime? GetTimeStartByType(DataTimeType type, DateTime time)   C#語法中一個個問號(?)的運算子是指:可以為 null

redis常見資料型別操作命令,Java使用Jedis操作Redis

redis常見資料型別操作命令 可參考地址:Http://redisdoc.com/ Java中使用Jedis操作Redis: https://www.cnblogs.com/liuling/p/2014-4-19-04.html redis鍵(key)

redis各種資料型別對應的jedis操作命令

一、常用資料型別簡介:       redis常用五種資料型別:string,hash,list,set,zset(sorted set). 1.String型別 String是最簡單的型別,一個key對應一個value String型別的資料最大1G。 String

c語言資料型別

c語言中的資料型別 "資料型別": { "基本資料型別":{ //%d 以整數型輸出 "整型":{ //int 4位元組 -

小白眼中的Python3.0資料型別List的相關操作

PS:本人小白,剛開始自學,先重在使用,再由淺入深,其中有現階段未領悟到的和筆誤,望前輩指出修正 :)         轉載也請註明出處哦~ 因為學完了Python中資料型別List的章節,所以對個別常用相關操作做個彙總,日後再慢慢增加,做個備忘 1. 建立List資料

C++11 —— 獲取 tuple 引數列表指定資料型別的索引位置

1. 問題背景   在 C++11 的標準中,我們可以通過 std::get< Index >(tuple) (以常量整數值為索引號)操作 tuple 中的引數,而到了 C++14 之後的標準,新增了 std::get< Type >(tuple) (以資料型別為索引)的方式操作 t

C# Bitmap和HalconHObject資料型別的相互轉換

              C# 中Bitmap和Halcon中HObject資料型別的相互轉換         public void Bitmap2HObjectBpp24(Bitm

C#讀取SharePoint的List資料操作SharePoint

一般SharePoint都有提供一個webservice來訪問、操作SharePoint的各項資料。 地址一般為http://server/_vti_bin/Lists.asmx 下面是一個簡單的讀取SharePoint List資料的例子:       &

檔案是資料(位元組)流的抽象-為什麼C++會把檔案操作抽象為fstream?

這不過是返祖罷了。正確的問題是為什麼會把資料流抽象成檔案。   裝置-位元組流-檔案。 一切皆為檔案,所有不同種類的型別都被抽象成檔案(比如:塊裝置,socket套接字,pipe佇列)。   檔案抽象為資料流一定程度上是 Unix 造成的。 傳統上,計算機上用