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

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>
              免费成人黄色片| 亚洲国产精品成人一区二区| 你懂的国产精品永久在线| 羞羞漫画18久久大片| 亚洲视频一区二区在线观看| 亚洲激情av| 亚洲毛片在线看| aa成人免费视频| 亚洲一区二区伦理| 欧美一区二区成人| 久久国产欧美| 亚洲图片在线| 好吊一区二区三区| 狠狠色狠狠色综合日日91app| 国产精品私人影院| 国产在线精品一区二区中文| 曰本成人黄色| 亚洲高清资源| 亚洲九九爱视频| 日韩午夜免费视频| 亚洲欧美激情在线视频| 亚洲一区免费网站| 最新国产成人在线观看| 欧美三级在线播放| 国产欧美日韩| 亚洲人成啪啪网站| 亚洲女人天堂av| 麻豆成人小视频| 日韩一级网站| 久久精品中文| 国产精品网站视频| 亚洲欧洲在线观看| 午夜国产一区| 亚洲激情在线视频| 欧美永久精品| 欧美性猛交xxxx乱大交蜜桃 | 欧美中文字幕在线播放| 亚洲成在人线av| 篠田优中文在线播放第一区| 欧美日韩成人一区二区| 一区二区亚洲精品国产| 亚洲一区二区三区在线| 欧美国产日韩一区二区在线观看| 亚洲天堂成人在线视频| 欧美电影在线观看完整版| 国产真实乱偷精品视频免| 亚洲一区二区综合| 亚洲日本中文字幕| 久久在线视频| 韩国三级电影久久久久久| 小黄鸭视频精品导航| 999在线观看精品免费不卡网站| 久久综合狠狠综合久久综青草| 亚洲精品日韩久久| 欧美伊人久久大香线蕉综合69| 欧美日韩在线高清| 午夜精品国产更新| 久久国产视频网站| 国产精品99久久久久久久久| 欧美国产综合视频| 亚洲国产综合视频在线观看| 老**午夜毛片一区二区三区| 欧美一区二区三区电影在线观看| 国产精品国产自产拍高清av| 在线视频亚洲| 日韩视频免费观看| 欧美日韩综合视频网址| 亚洲视频综合| 一本久久知道综合久久| 国产精品国产三级国产专播精品人 | 亚洲欧美日韩国产成人| 国产精品色在线| 香蕉成人久久| 亚洲欧美区自拍先锋| 国产欧美日韩激情| 久久久一区二区| 久久最新视频| 一区二区三区日韩欧美| 亚洲一区国产一区| 国产主播一区二区三区四区| 久久久久久亚洲综合影院红桃| 亚洲欧美日本日韩| 国内视频精品| 亚洲国产成人精品女人久久久| 蜜桃av一区二区在线观看| 99精品99| 午夜国产精品视频| 狠狠色噜噜狠狠狠狠色吗综合| 久久精品视频在线播放| 亚洲一线二线三线久久久| 国产精品日韩专区| 狂野欧美一区| 欧美激情视频在线免费观看 欧美视频免费一| 91久久精品网| 中日韩美女免费视频网址在线观看| 欧美午夜片在线观看| 久久亚洲国产精品一区二区| 欧美国产精品日韩| 欧美有码在线观看视频| 久久夜色精品国产欧美乱| 一本综合久久| 欧美在线视频一区二区| 日韩视频免费大全中文字幕| 亚洲在线观看视频| 日韩视频在线永久播放| 亚洲欧美日韩一区在线| 亚洲精品国产精品乱码不99| 亚洲欧美激情视频在线观看一区二区三区| 国产亚洲欧美日韩美女| 最新精品在线| 亚洲欧美另类在线| 亚洲精品午夜精品| 性视频1819p久久| 日韩一级黄色大片| 久久久久国产成人精品亚洲午夜| 亚洲视频一起| 欧美大尺度在线观看| 欧美一区亚洲| 欧美大片在线观看一区| 久久国产精品网站| 国产精品卡一卡二| 亚洲美女免费精品视频在线观看| 激情成人av| 亚洲欧美日韩电影| 日韩视频免费在线观看| 久久全球大尺度高清视频| 亚洲一区二区三区视频播放| 欧美成人精品h版在线观看| 久久久久久高潮国产精品视| 欧美日韩成人| 久久综合福利| 国产综合激情| 亚洲欧美视频一区| 亚洲午夜av在线| 欧美日韩激情网| 亚洲精品视频免费观看| 亚洲电影免费观看高清完整版| 欧美亚洲自偷自偷| 性做久久久久久久免费看| 国产精品v日韩精品v欧美精品网站| 亚洲狠狠婷婷| 亚洲精品日韩欧美| 欧美.www| 欧美a级片网站| 精品99一区二区三区| 久久精品国产一区二区三区| 久久精品91久久久久久再现| 亚洲小说春色综合另类电影| 一区二区三区偷拍| 欧美三区在线观看| 一级日韩一区在线观看| 亚洲欧美激情四射在线日| 国产精品久久毛片a| 亚洲午夜国产一区99re久久| 亚洲欧美另类在线观看| 国产精品v欧美精品v日本精品动漫 | 久久在线免费视频| 国产一区二区三区在线观看免费视频| 中文精品99久久国产香蕉| 亚洲一二三区在线| 国产精品一区二区三区观看| 先锋影音一区二区三区| 久久综合色婷婷| 亚洲精品日韩在线| 欧美三级电影网| 亚洲女人小视频在线观看| 久久久久久久高潮| 亚洲清纯自拍| 国产精品成人在线| 久久精品视频一| 亚洲乱码一区二区| 欧美一区二区三区精品电影| 精品成人一区| 欧美不卡一区| 久久精品成人一区二区三区| 亚洲与欧洲av电影| 老司机凹凸av亚洲导航| 一区二区高清在线| 国产三区精品| 欧美国产日韩一区| 亚洲自拍偷拍一区| 另类av一区二区| 蜜臀va亚洲va欧美va天堂| 日韩一级黄色大片| 欧美va亚洲va国产综合| 亚洲第一视频网站| 久久国产天堂福利天堂| 国产精品日韩欧美一区二区| 一道本一区二区| 亚洲精品资源美女情侣酒店| 欧美v日韩v国产v| 国产综合精品| 欧美国产精品一区| 欧美成人免费大片| 久久国产一二区| 久久国产精品久久w女人spa| 国产亚洲一二三区| 亚洲激情黄色| 欧美激情亚洲| 亚洲永久网站|