青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

C++ Programmer's Cookbook

{C++ 基礎(chǔ)} {C++ 高級} {C#界面,C++核心算法} {設(shè)計模式} {C#基礎(chǔ)}

C++多線程(九)

多線程之線程局部存儲

一 線程局部存儲 (TLS)
      來自:http://msdn2.microsoft.com/en-us/library/ms686749.aspx
      
      同一進(jìn)程中的所有線程共享相同的虛擬地址空間。不同的線程中的局部變量有不同的副本,但是static和globl變量是同一進(jìn)程中的所有線程共享的。使用TLS技術(shù)可以為static和globl的變量,根據(jù)當(dāng)前進(jìn)程的線程數(shù)量創(chuàng)建一個array,每個線程可以通過array的index來訪問對應(yīng)的變量,這樣也就保證了static和global的變量為每一個線程都創(chuàng)建不同的副本。

二 線程局部存儲(TLS)實現(xiàn)使用

1)TLS 的 API 實現(xiàn)
通過 Win32 API 層和編譯器實現(xiàn)“線程本地存儲”。有關(guān)詳細(xì)信息,請參見 Win32 API 文檔中的 TlsAlloc、TlsGetValue、TlsSetValue 和 TlsFree。 (下面的代碼對msdn的稍加修改)

#include <windows.h> 
#include 
<stdio.h> 

#define THREADCOUNT 10
DWORD dwTlsIndex;  

static int g_x = 100;  // test static var for multiple threading

VOID ErrorExit(LPSTR); 

VOID CommonFunc(VOID) 

    LPVOID lpvData; 

    
// Retrieve a data pointer for the current thread. 
    lpvData = TlsGetValue(dwTlsIndex); 
    
if ((lpvData == 0&& (GetLastError() != ERROR_SUCCESS)) 
        ErrorExit(
"TlsGetValue error"); 
    
int *pg_x = (int*)lpvData;

    
// Use the data stored for the current thread. 
    printf("thread %d: g_x adress=%lx,g_x copy ++ = %d\n", GetCurrentThreadId(), pg_x, *pg_x);
    Sleep(
1000); 
}
 

DWORD WINAPI ThreadFunc(VOID) 

    LPVOID lpvData; 

    
// Initialize the TLS index for this thread. 
    lpvData = (LPVOID) LocalAlloc(LPTR, 256); 
    
//*(int*)lpvData = g_x;
    int *pg_x = (int*)lpvData;
    
*pg_x = g_x;
    
if (! TlsSetValue(dwTlsIndex, lpvData)) 
        ErrorExit(
"TlsSetValue error");     

    printf(
"thread %d: g_x adress=%lx,g_x copy = %d\n", GetCurrentThreadId(), pg_x, *pg_x);

    InterlockedExchangeAdd(reinterpret_cast
<long*>(pg_x),1);
    CommonFunc(); 

    
// Release the dynamic memory before the thread returns.
    lpvData = TlsGetValue(dwTlsIndex); 
    
if (lpvData != 0
        LocalFree((HLOCAL) lpvData); 

    
return 0
}
 

int main(VOID) 

    DWORD IDThread; 
    HANDLE hThread[THREADCOUNT]; 
    
int i; 

    printf(
"main thread: g_x is :%d\n",g_x);

    
// Allocate a TLS index.  
    if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
        ErrorExit(
"TlsAlloc failed"); 

    
//test for multiple static or global var
    int dwTlsIndex2 = TlsAlloc();

    
// Create multiple threads. 
    for (i = 0; i < THREADCOUNT; i++
    

        hThread[i] 
= CreateThread(NULL, // default security attributes 
            0,                           // use default stack size 
            (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function 
            NULL,                    // no thread function argument 
            0,                       // use default creation flags 
            &IDThread);              // returns thread identifier 

        
// Check the return value for success. 
        if (hThread[i] == NULL) 
            ErrorExit(
"CreateThread error\n"); 
    }
 

    
for (i = 0; i < THREADCOUNT; i++
        WaitForSingleObject(hThread[i], INFINITE); 

    TlsFree(dwTlsIndex);

    printf(
"main thread: g_x is :%d\n",g_x);
    
return 0
}
 

VOID ErrorExit (LPSTR lpszMessage) 

    fprintf(stderr, 
"%s\n", lpszMessage); 
    ExitProcess(
0); 
}

運(yùn)行結(jié)果:(可以看出不同的線程中的g_x變量有不同的地址,即有不同的拷貝,主線程變量在開始和最后都不被改變。)

PS: (TLS的實現(xiàn)原理與API解釋)
1:在多線程的進(jìn)程中,為每一個static或是global變量創(chuàng)建一個void*的數(shù)組,使變量不同線程都有一個拷貝,然后拷貝的指針放入Void*的數(shù)組中。
2:TlsAlloc() 得到對應(yīng)于一個static或global變量所對應(yīng)的void*的數(shù)組的索引,這個用來標(biāo)示不同static或global變量所對應(yīng)的void*的數(shù)組。
3:LocalAlloc()用來分配變量在此線程的拷貝的指針。
4:TlsSetValue()用來把剛分配的指針加到所對應(yīng)的void*的數(shù)組中,加入后一般對會對此指針賦值供此線程使用。
5:TlsGetValue()用來從對應(yīng)的void*的數(shù)組中找到此線程所對應(yīng)的拷貝的指針。
6:   TlsFree() 釋放整個void*的指針數(shù)組。



2)TLS 的編譯器實現(xiàn)

為了支持 TLS,已將新屬性 thread 添加到了 C 和 C++ 語言,并由 Visual C++ 編譯器支持。使用 __declspec 關(guān)鍵字聲明 thread 變量。例如,以下代碼聲明了一個整數(shù)線程局部變量,并用一個值對其進(jìn)行初始化:

__declspec( thread ) int tls_i = 1;

下面的代碼使用了VC提供的__declspec關(guān)鍵字來實現(xiàn)TLS,如果不使用TLS,全局變量g_x則在線程中輸出的結(jié)果都是1,2,3,4.。。。但是如果使用TLS則在線程中輸出的結(jié)果都是1,編譯器幫我們保證了每個線程都有一個副本。 我們還可以看到主線程在開始和最后輸出的g_x都是0,這也更說明了在線程有不同的副本。

#include <windows.h> 
#include 
<stdio.h> 
 
#define THREADCOUNT 10

DWORD dwTlsIndex; 

//static int g_x = 0;

#define Thread  __declspec(thread)
Thread 
static int g_x = 0;

VOID ErrorExit(LPSTR); 
 
DWORD WINAPI ThreadFunc(VOID) 

  InterlockedExchangeAdd(reinterpret_cast
<long*>(&g_x),1); //g_x+=1;   
  printf("thread id: %d, g_x++ = %d, g_x adress = %d\n",GetCurrentThreadId(),g_x,&g_x);
  
return 1
}
 
 
int main(VOID) 

   DWORD IDThread; 
   HANDLE hThread[THREADCOUNT]; 
   
int i; 

    printf(
"main thread: g_x = %d, g_x adress = %d\n",g_x,&g_x);
  
// Create multiple threads.  
   for (i = 0; i < THREADCOUNT; i++
   

      hThread[i] 
= CreateThread(NULL, // default security attributes 
         0,                           // use default stack size 
         (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function 
         NULL,                    // no thread function argument 
         0,                       // use default creation flags 
         &IDThread);              // returns thread identifier 
 
   
// Check the return value for success. 
      if (hThread[i] == NULL) 
         ErrorExit(
"CreateThread error\n"); 
   }
 
 
   
for (i = 0; i < THREADCOUNT; i++
      WaitForSingleObject(hThread[i], INFINITE); 
  

   printf(
"main thread: g_x = %d, g_x adress = %d\n",g_x,&g_x);
   
return 0
}
 
 
VOID ErrorExit (LPSTR lpszMessage) 

   fprintf(stderr, 
"%s\n", lpszMessage); 
   ExitProcess(
0); 

運(yùn)行結(jié)果:

三 使用VC關(guān)鍵字實現(xiàn)TLS需要注意:

聲明靜態(tài)綁定線程的本地對象和變量時必須遵守下列原則:

  • thread 屬性只能應(yīng)用于數(shù)據(jù)聲明和定義。它不能用于函數(shù)聲明或定義。例如,以下代碼將生成一個編譯器錯誤:
    #define Thread  __declspec( thread )
        Thread void func();     // This will generate an error.
  • 只能在具有 static 作用域的數(shù)據(jù)項上指定 thread 修飾符。包括全局?jǐn)?shù)據(jù)對象(包括 staticextern)、本地靜態(tài)對象和 C++ 類的靜態(tài)數(shù)據(jù)成員。不可以用 thread 屬性聲明自動數(shù)據(jù)對象。以下代碼將生成編譯器錯誤:
    #define Thread  __declspec( thread )
        void func1()
        {
        Thread int tls_i;            // This will generate an error.
        }
        int func2( Thread int tls_i )    // This will generate an error.
        {
        return tls_i;
        }
  • 線程本地對象的聲明和定義必須全都指定 thread 屬性。例如,以下代碼將生成錯誤:
    #define Thread  __declspec( thread )
        extern int tls_i;        // This will generate an error, since the
        int Thread tls_i;        // declaration and definition differ.
  • thread 屬性不能用作類型修飾符。例如,以下代碼將生成一個編譯器錯誤:
    char __declspec( thread ) *ch;        // Error
  • C++ 類不能使用 thread 屬性。但是,可以使用 thread 屬性將 C++ 類對象實例化。例如,以下代碼將生成一個編譯器錯誤:
    #define Thread  __declspec( thread )
        class Thread C       // Error: classes cannot be declared Thread.
        {
        // Code
        };
        C CObject;

    因為允許使用 thread 屬性的 C++ 對象的聲明,因此下面兩個示例在語義上是等效的:

    #define Thread  __declspec( thread )
        Thread class B
        {
        // Code
        } BObject;               // OK--BObject is declared thread local.
        class B
        {
        // Code
        };
        Thread B BObject;        // OK--BObject is declared thread local.
  • 不將線程本地對象的地址視為常數(shù),并且涉及此類地址的任何表達(dá)式都不視為常數(shù)。在標(biāo)準(zhǔn) C 中,這種作法的效果是禁止將線程本地變量的地址用作對象或指針的初始值設(shè)定項。例如,C 編譯器將以下代碼標(biāo)記為錯誤:
    #define Thread  __declspec( thread )
        Thread int tls_i;
        int *p = &tls_i;       //This will generate an error in C.

    但是,此限制不適用于 C++。因為 C++ 允許動態(tài)初始化所有對象,因此可以用使用線程本地變量地址的表達(dá)式初始化對象。實現(xiàn)此操作的方式與實現(xiàn)線程本地對象結(jié)構(gòu)的方式相同。例如,以上顯示的代碼在作為 C++ 源文件編譯時不會生成錯誤。請注意:只有在其中獲取地址的線程仍然存在的情況下,線程本地變量的地址才有效。

  • 標(biāo)準(zhǔn) C 允許使用涉及引用自身的表達(dá)式初始化對象或變量,但只適用于非靜態(tài)作用域的對象。雖然 C++ 通常允許使用涉及引用自身的表達(dá)式動態(tài)初始化對象,但是這種類型的初始化不允許用于線程本地對象。例如:
    #define Thread  __declspec( thread )
        Thread int tls_i = tls_i;                // Error in C and C++
        int j = j;                               // OK in C++, error in C
        Thread int tls_i = sizeof( tls_i )       // Legal in C and C++

    請注意:包含正在初始化的對象的 sizeof 表達(dá)式不建立對自身的引用且在 C 和 C++ 中都是合法的。

    C++ 不允許此類對線程數(shù)據(jù)的動態(tài)初始化,因為將來可能要對線程本地存儲功能進(jìn)行增強(qiáng)。

  • 如果 DLL 將任何非本地數(shù)據(jù)或?qū)ο舐暶鳛?__declspec(線程),動態(tài)加載該 DLL 時會導(dǎo)致保護(hù)錯誤。使用 LoadLibrary 加載所有 DLL 后,每當(dāng)代碼引用非本地 __declspec(線程)數(shù)據(jù)時,將導(dǎo)致系統(tǒng)故障。由于線程的全局變量空間是在運(yùn)行時分配的,因此此空間的大小是以應(yīng)用程序的需求和所有靜態(tài)鏈接的 DLL 的需求相加為基礎(chǔ)計算出來的。使用 LoadLibrary 時,無法擴(kuò)展此空間以允許放置用 __declspec(線程)聲明的線程本地變量。如果 DLL 可能是用 LoadLibrary 加載的,請在 DLL 中使用 TLS API(如 TlsAlloc)來分配 TLS。

四  DLL使用TLS :http://msdn2.microsoft.com/en-us/library/ms686997.aspx
    

posted on 2007-08-01 15:38 夢在天涯 閱讀(5976) 評論(0)  編輯 收藏 引用 所屬分類: CPlusPlus

公告

EMail:itech001#126.com

導(dǎo)航

統(tǒng)計

  • 隨筆 - 461
  • 文章 - 4
  • 評論 - 746
  • 引用 - 0

常用鏈接

隨筆分類

隨筆檔案

收藏夾

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

積分與排名

  • 積分 - 1813332
  • 排名 - 5

最新評論

閱讀排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              亚洲欧美日韩国产成人| 欧美视频在线免费| 激情欧美一区| 免费91麻豆精品国产自产在线观看| 欧美一区二区私人影院日本| 国产麻豆午夜三级精品| 久久久亚洲午夜电影| 久久躁日日躁aaaaxxxx| 亚洲精品欧美| 亚洲一区二区三区在线视频| 一区二区精品国产| 亚洲在线观看免费| 国产亚洲福利| 亚洲成人资源| 欧美91精品| 亚洲欧美在线免费观看| 久久久久免费视频| 亚洲先锋成人| 久久精品国产欧美亚洲人人爽| 最新高清无码专区| 亚洲女ⅴideoshd黑人| 亚洲第一久久影院| 在线视频欧美一区| 亚洲高清不卡在线观看| 一区二区不卡在线视频 午夜欧美不卡'| 国产精品一区二区你懂得 | 亚洲黄色在线| 亚洲午夜精品久久| 91久久久在线| 午夜一区二区三视频在线观看| 亚洲国产一区二区三区高清| 国产精品99久久久久久白浆小说| 尤物在线精品| 亚洲欧美国产精品桃花| 一区二区免费在线观看| 久久精品伊人| 欧美一区二区三区久久精品 | 在线视频一区观看| 亚洲国产一区二区三区在线播| 亚洲一二三四久久| av成人免费在线观看| 久久国产精品一区二区三区| 亚洲中无吗在线| 欧美电影免费观看高清完整版| 久久久久久久一区二区三区| 国产精品第2页| 亚洲精品极品| 亚洲人成在线播放| 老司机免费视频一区二区| 欧美在线一级视频| 国产精品热久久久久夜色精品三区| 欧美激情视频给我| 亚洲高清在线观看| 久久亚洲精品一区二区| 久久婷婷成人综合色| 国产日韩成人精品| 亚洲一线二线三线久久久| 亚洲在线黄色| 国产精品久久久久久超碰| 99国产精品99久久久久久粉嫩| 91久久嫩草影院一区二区| 免费在线观看日韩欧美| 欧美成人免费全部| 亚洲国产三级网| 免费亚洲电影在线观看| 欧美激情第4页| 亚洲美女视频网| 欧美精品一区二区三| 亚洲精品一区二区三区樱花 | 国产一区二区三区成人欧美日韩在线观看| 亚洲视频第一页| 午夜国产不卡在线观看视频| 国产精品一二三四区| 欧美日韩视频一区二区| 日韩视频一区二区三区| 中文一区在线| 国产精品网站在线观看| 香蕉久久夜色精品国产| 久久免费精品视频| 在线日韩av片| 欧美日本三区| 亚洲一区二区三区乱码aⅴ| 欧美一区二区三区免费观看视频| 国产欧美一区二区色老头| 久久九九国产| 亚洲欧洲一区二区在线播放| 亚洲综合二区| 国精品一区二区| 欧美成人一区二区在线 | 久久精品一二三区| 亚洲第一主播视频| 欧美日韩在线直播| 欧美在线观看一区| 亚洲人成人一区二区在线观看| 亚洲尤物精选| 亚洲高清久久久| 国产精品对白刺激久久久| 欧美主播一区二区三区美女 久久精品人 | 欧美一区观看| 亚洲日韩欧美视频| 国产毛片精品国产一区二区三区| 久久青草欧美一区二区三区| 亚洲免费成人| 欧美成人精品在线| 午夜国产精品影院在线观看| 亚洲电影av在线| 国产精品卡一卡二| 欧美激情视频一区二区三区在线播放| 亚洲免费视频一区二区| 欧美激情综合| 久久综合九色| 午夜激情综合网| 99精品欧美一区| 一区二区三区在线观看视频| 国产精品mm| 欧美国产日韩精品| 久久久久久免费| 亚洲欧美中文另类| 9久草视频在线视频精品| 欧美成人免费全部| 久久人人97超碰精品888| 亚洲在线观看视频网站| 亚洲毛片在线看| 亚洲国产精品久久久久婷婷老年 | 欧美日韩中文字幕在线| 免费观看不卡av| 久久久中精品2020中文| 欧美一区二区观看视频| 亚洲伊人伊色伊影伊综合网| 最新日韩中文字幕| 欧美激情网站在线观看| 可以看av的网站久久看| 欧美诱惑福利视频| 欧美一区三区二区在线观看| 亚洲在线黄色| 亚洲欧美第一页| 亚洲性图久久| 亚洲一区999| 亚洲欧美高清| 亚洲综合日韩中文字幕v在线| 久久综合久久综合九色| 一区二区三区四区国产精品| 日韩一级欧洲| 亚洲少妇自拍| 亚洲综合清纯丝袜自拍| 亚洲欧美中文另类| 欧美一区二区三区免费大片| 香蕉乱码成人久久天堂爱免费| 午夜伦欧美伦电影理论片| 欧美亚洲在线| 久久久精品国产一区二区三区| 久久免费一区| 欧美国产免费| 国产精品国产精品| 国产伦精品一区二区三区高清 | 久久精品一二三区| 久久尤物视频| 欧美激情一区二区在线 | 欧美视频网址| 国产精品一区视频| 精品盗摄一区二区三区| 亚洲人www| 亚洲影视综合| 久久久久久亚洲精品杨幂换脸 | 免播放器亚洲| 日韩小视频在线观看| 亚洲欧美激情四射在线日| 久久精品国产视频| 欧美日本一道本| 国产精品永久入口久久久| 黄色成人av| 亚洲无限乱码一二三四麻| 久久高清国产| 亚洲精品123区| 香蕉国产精品偷在线观看不卡 | 欧美一区二区三区视频免费播放| 久久综合伊人77777蜜臀| 欧美日本一区二区视频在线观看| 国产精品视频久久一区| 亚洲国产成人久久| 亚洲欧美日韩专区| 欧美国产亚洲另类动漫| 亚洲系列中文字幕| 男女视频一区二区| 国产日韩一区二区三区在线| 亚洲精品视频一区| 久久精品伊人| 在线视频日韩精品| 欧美高清视频在线观看| 国产婷婷一区二区| 亚洲手机视频| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲线精品一区二区三区八戒| 久久久欧美精品| 在线亚洲免费| 欧美精品久久久久久久久老牛影院| 国产香蕉97碰碰久久人人| 中国女人久久久| 91久久黄色| 免费精品视频|