那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于多線程環(huán)境下值的遞增操作--原子操作

C語言專家集中營 ? 2018-01-10 11:16 ? 次閱讀

為了描述方便和代碼簡潔起見,我們可以只輸出最后的報數(shù)結(jié)果來觀察程序是否運行出錯。這也非常類似于統(tǒng)計一個網(wǎng)站每天有多少用戶登錄,每個用戶登錄用一個線程模擬,線程運行時會將一個表示計數(shù)的變量遞增。程序在最后輸出計數(shù)的值表示有今天多少個用戶登錄,如果這個值不等于我們啟動的線程個數(shù),那顯然說明這個程序是有問題的。整個程序代碼如下:

[cpp]view plaincopy

#include

#include

#include

volatilelongg_nLoginCount;//登錄次數(shù)

unsignedint__stdcallFun(void*pPM);//線程函數(shù)

constintTHREAD_NUM=10;//啟動線程數(shù)

unsignedint__stdcallThreadFun(void*pPM)

{

Sleep(100);//someworkshouldtodo

g_nLoginCount++;

Sleep(50);

return0;

}

intmain()

{

g_nLoginCount=0;

HANDLEhandle[THREAD_NUM];

for(inti=0;i

handle[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,NULL);

WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);

printf("有%d個用戶登錄后記錄結(jié)果是%d\n",THREAD_NUM,g_nLoginCount);

return0;

}

程序中模擬的是10個用戶登錄,程序?qū)⑤敵鼋Y(jié)果:

基于多線程環(huán)境下值的遞增操作--原子操作

和上一篇的線程報數(shù)程序一樣,程序輸出的結(jié)果好象并沒什么問題。下面我們增加點用戶來試試,現(xiàn)在模擬50個用戶登錄,為了便于觀察結(jié)果,在程序中將50個用戶登錄過程重復(fù)20次,代碼如下:

[cpp]view plaincopy

#include

#include

volatilelongg_nLoginCount;//登錄次數(shù)

unsignedint__stdcallFun(void*pPM);//線程函數(shù)

constDWORDTHREAD_NUM=50;//啟動線程數(shù)

DWORDWINAPIThreadFun(void*pPM)

{

Sleep(100);//someworkshouldtodo

g_nLoginCount++;

Sleep(50);

return0;

}

intmain()

{

printf("原子操作Interlocked系列函數(shù)的使用\n");

printf("--byMoreWindows(http://blog.csdn.net/MoreWindows)--\n\n");

//重復(fù)20次以便觀察多線程訪問同一資源時導(dǎo)致的沖突

intnum=20;

while(num--)

{

g_nLoginCount=0;

inti;

HANDLEhandle[THREAD_NUM];

for(i=0;i

handle[i]=CreateThread(NULL,0,ThreadFun,NULL,0,NULL);

WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);

printf("有%d個用戶登錄后記錄結(jié)果是%d\n",THREAD_NUM,g_nLoginCount);

}

return0;

}

運行結(jié)果如下圖:

基于多線程環(huán)境下值的遞增操作--原子操作

現(xiàn)在結(jié)果水落石出,明明有50個線程執(zhí)行了g_nLoginCount++;操作,但結(jié)果輸出是不確定的,有可能為50,但也有可能小于50。

要解決這個問題,我們就分析下g_nLoginCount++;操作。在VC6.0編譯器對g_nLoginCount++;這一語句打個斷點,再按F5進(jìn)入調(diào)試狀態(tài),然后按下Debug工具欄的Disassembly按鈕,這樣就出現(xiàn)了匯編代碼窗口??梢园l(fā)現(xiàn)在C/C++語言中一條簡單的自增語句其實是由三條匯編代碼組成的,如下圖所示。

基于多線程環(huán)境下值的遞增操作--原子操作

講解下這三條匯編意思:

第一條匯編將g_nLoginCount的值從內(nèi)存中讀取到寄存器eax中。

第二條匯編將寄存器eax中的值與1相加,計算結(jié)果仍存入寄存器eax中。

第三條匯編將寄存器eax中的值寫回內(nèi)存中。

這樣由于線程執(zhí)行的并發(fā)性,很可能線程A執(zhí)行到第二句時,線程B開始執(zhí)行,線程B將原來的值又寫入寄存器eax中,這樣線程A所主要計算的值就被線程B修改了。這樣執(zhí)行下來,結(jié)果是不可預(yù)知的——可能會出現(xiàn)50,可能小于50。

因此在多線程環(huán)境中對一個變量進(jìn)行讀寫時,我們需要有一種方法能夠保證對一個值的遞增操作是原子操作——即不可打斷性,一個線程在執(zhí)行原子操作時,其它線程必須等待它完成之后才能開始執(zhí)行該原子操作。這種涉及到硬件的操作會不會很復(fù)雜了,幸運的是,Windows系統(tǒng)為我們提供了一些以Interlocked開頭的函數(shù)來完成這一任務(wù)(下文將這些函數(shù)稱為Interlocked系列函數(shù))。

下面列出一些常用的Interlocked系列函數(shù):

1.增減操作

LONG__cdeclInterlockedIncrement(LONGvolatile*Addend);

LONG__cdeclInterlockedDecrement(LONGvolatile*Addend);

返回變量執(zhí)行增減操作之后的值。

LONG__cdecInterlockedExchangeAdd(LONGvolatile*Addend,LONGValue);

返回運算后的值,注意!加個負(fù)數(shù)就是減。

2.賦值操作

LONG__cdeclInterlockedExchange(LONGvolatile*Target,LONGValue);

Value就是新值,函數(shù)會返回原先的值。

在本例中只要使用InterlockedIncrement()函數(shù)就可以了。將線程函數(shù)代碼改成:

[cpp]view plaincopy

DWORDWINAPIThreadFun(void*pPM)

{

Sleep(100);//someworkshouldtodo

//g_nLoginCount++;

InterlockedIncrement((LPLONG)&g_nLoginCount);

Sleep(50);

return0;

}

再次運行,可以發(fā)現(xiàn)結(jié)果會是唯一的。

基于多線程環(huán)境下值的遞增操作--原子操作

因此,在多線程環(huán)境下,我們對變量的自增自減這些簡單的語句也要慎重思考,防止多個線程導(dǎo)致的數(shù)據(jù)訪問出錯。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 多線程
    +關(guān)注

    關(guān)注

    0

    文章

    278

    瀏覽量

    20075
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2114

    瀏覽量

    73859
  • 遞增
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    6703

原文標(biāo)題:原子操作 Interlocked系列函數(shù)

文章出處:【微信號:C_Expert,微信公眾號:C語言專家集中營】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    一文詳解RTOS開發(fā)中的原子操作

    裸機開發(fā)與RTOS開發(fā)一個非常重要的區(qū)別在于多線程之間的消息傳遞和數(shù)據(jù)共享問題,然而在這中間變量的原子操作是一個非常重要的話題,不同的處理器架構(gòu)和編譯選項都可能生成不同的指令,從而影響到變量的
    發(fā)表于 11-17 09:43 ?1779次閱讀

    Java多線程的用法

    本文將介紹一Java多線程的用法。 基礎(chǔ)介紹 什么是多線程 指的是在一個進(jìn)程中同時運行多個線程,每個線程都可以獨立執(zhí)行不同的任務(wù)或
    的頭像 發(fā)表于 09-30 17:07 ?1006次閱讀

    LabView的多線程語言

    LabView的多線程語言以前只會照貓畫虎的寫一些簡單的程序,一些基本原理不是很清晰。從網(wǎng)上找了一些資料,這里總結(jié)一。1。一般情況,運行一個 VI,至少有兩個線程:一個界面
    發(fā)表于 06-08 10:13

    原子操作指令的作用

    一些常用操作原子指令,這些原子指令的作用就是避免多線程同時對一個共享數(shù)據(jù)進(jìn)行讀寫操作。沒有原子
    發(fā)表于 09-18 21:39

    Java操作系統(tǒng)支持多線程

    Windows等操作系統(tǒng)均支持多線程進(jìn)程的并發(fā)處理機制。操作系統(tǒng)支持多線程,使多個程序能夠并發(fā)執(zhí)行,以改善資源使用率和提高系統(tǒng)效率;操作系統(tǒng)
    發(fā)表于 08-05 06:06

    基于51單片機的多線程操作系統(tǒng) 精選資料分享

    我知道,在51單片機上運行一個操作系統(tǒng),大多數(shù)情況并不實用。但51單片機廣為人知。所以我認(rèn)為,用它來逐步的實現(xiàn)一個多線程操作系統(tǒng),使得讀者以更多的精力思考
    發(fā)表于 07-20 07:55

    如何使用多線程和異步操作等并發(fā)設(shè)計方法來最大化程序的性能

    (超線程、雙核)的普及,多線程和異步操作等并發(fā)程序設(shè)計方法也受到了更多的關(guān)注和討論。本文主要是想探討一如何使用并發(fā)來最大化程序的性能。  多線程
    發(fā)表于 08-23 16:31

    在MCU開發(fā)中使用多線程操作一寫一讀是否需要保護(hù)?

    ,那么多線程訪問是安全的,那么對于一寫一讀,在某些情況需要保護(hù),某些情況其實可以不需要保護(hù)。當(dāng)操作數(shù)據(jù)是 1字節(jié) uint8_t 類型數(shù)據(jù),可以不做保護(hù),對于uint8_t類型的數(shù)
    發(fā)表于 02-01 15:42

    QNX環(huán)境多線程編程

    介紹了QNX 實時操作系統(tǒng)和多線程編程技術(shù),包括線程間同步的方法、多線程程序的分析步驟、線程基本程序結(jié)構(gòu)以及實用編譯方法。QNX 是由加拿大
    發(fā)表于 08-12 17:37 ?30次下載

    MFC多線程編程

    計算機上的上位機制作工具語言之MFC多線程編程
    發(fā)表于 09-01 14:55 ?0次下載

    linux多線程編程技術(shù)

    1 引言 線程(thread)技術(shù)早在60年代就被提出,但真正應(yīng)用多線程操作系統(tǒng)中去,是在80年代中期,solaris是這方面的佼佼者。傳統(tǒng)的 Unix也支持線程的概念,但是在一個進(jìn)
    發(fā)表于 10-24 16:01 ?5次下載

    Linux多線程編程

    線程呢?使用多線程到底有哪些好處?什么的系統(tǒng)應(yīng)該選用多線程?我們首先必須回答這些問題?! ∈褂?b class='flag-5'>多線程的理由之一是和進(jìn)程相比,它是一種非常"節(jié)儉"的多任務(wù)
    發(fā)表于 04-02 14:43 ?637次閱讀

    多線程的情況如何對一個進(jìn)行 a++ 操作

    多線程的情況,對一個進(jìn)行 a++ 操作,會出現(xiàn)什么問題? a++ 的問題 先寫個 demo 的例子。把 a++ 放入多線程中運行一
    的頭像 發(fā)表于 10-13 11:17 ?772次閱讀
    在<b class='flag-5'>多線程</b>的情況<b class='flag-5'>下</b>如何對一個<b class='flag-5'>值</b>進(jìn)行 a++ <b class='flag-5'>操作</b>

    多線程如何保證數(shù)據(jù)的同步

    。本文將詳細(xì)介紹多線程數(shù)據(jù)同步的概念、問題、以及常見的解決方案。 一、多線程數(shù)據(jù)同步概念 在多線程編程中,數(shù)據(jù)同步指的是通過某種機制來確保多個線程對共享數(shù)據(jù)的
    的頭像 發(fā)表于 11-17 14:22 ?1318次閱讀

    redis使用多線程處理操作命令

    討 Redis 多線程處理操作命令的實現(xiàn)和優(yōu)勢,幫助讀者深入了解這一方面的知識。 首先,我們來了解一 Redis 的基本概念和工作原理。Redis 是一個支持鍵值對存儲的數(shù)據(jù)庫系統(tǒng),它將數(shù)據(jù)存儲在內(nèi)存中,從而實現(xiàn)了高速讀寫
    的頭像 發(fā)表于 12-05 10:25 ?611次閱讀
    大发888真钱娱乐| 巢湖市| 澳门百家乐官网赌场娱乐网规则| 百家乐娱乐场开户注册| 百家乐官网什么牌最大| 百家乐烫金筹码| 利来网| 澳门百家乐官网必赢看| 大发888娱乐城 casino| 百家乐官网免费送现金| 大发888技巧| 澳门百家乐官网是骗人的| 扑克王百家乐的玩法技巧和规则| 周口市| 百家乐赌场破解方法| 百家乐官网里面的奥妙| 百家乐发牌盒子| 博彩百家乐官网龙虎| 线上百家乐怎么玩| 百家乐官网游戏客户端| 大发888真钱娱乐下载| 百家乐官网游戏卡通| 大发888娱乐场下载远程| 百家乐官网是真人发牌吗| 二八杠口诀| 新澳博百家乐娱乐城| 澳门百家乐官网真人娱乐城| 百家乐平注法到| 时时博百家乐官网的玩法技巧和规则 | 武汉百家乐赌具| 现场百家乐官网电话投注| 博彩百家乐五2013124预测| 新天地百家乐官网的玩法技巧和规则| 香港六合彩报| 百家乐博彩开户博彩通| 澳门百家乐官网技巧皇冠网| 大发888娱乐出纳柜台| 手机百家乐官网能兑换现金棋牌游戏 | 收藏| 帝王百家乐的玩法技巧和规则| 百家乐官网博娱乐网赌百家乐官网|