程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> CreateThread()與beginthread()的差別具體解析

CreateThread()與beginthread()的差別具體解析

編輯:關於C++

CreateThread()與beginthread()的差別具體解析。本站提示廣大學習愛好者:(CreateThread()與beginthread()的差別具體解析)文章只能為提供參考,不一定能成為您想要的結果。以下是CreateThread()與beginthread()的差別具體解析正文


我們曉得在Windows下創立一個線程的辦法有兩種,一種就是挪用Windows API CreateThread()來創立線程;別的一種就是挪用MSVC CRT的函數_beginthread()或_beginthreadex()來創立線程。響應的加入線程也有兩個函數Windows API的ExitThread()和CRT的_endthread()。這兩套函數都是用來創立和加入線程的,它們有甚麼差別呢?

許多開辟者不清晰這二者之間的關系,他們隨便選一個函數來用,發明也沒有甚麼年夜成績,因而就忙於處理更加緊急的義務去了,而沒有對它們停止深究。比及有一天溘然發明一個法式運轉時光很長的時刻會有纖細的內存洩漏,開辟者相對不會想到是由於這兩套函數用混的成果。

依據Windows API和MSVC CRT的關系,可以看出來_beginthread()是對CreateThread()的包裝,它終究照樣挪用CreateThread()來創立線程。那末在_beginthread()挪用CreateThread()之前做了甚麼呢?我們可以看一下_beginthread()的源代碼,它位於CRT源代碼中的thread.c。我們可以發明它在挪用CreateThread()之前請求了一個叫_tiddata的構造,然後將這個構造用_initptd()函數初始化以後傳遞給_beginthread()本身的線程進口函數_threadstart。_threadstart起首把由_beginthread()傳過去的_tiddata構造指針保留到線程的顯式TLS數組,然後它挪用用戶的線程進口真正開端線程。在用戶線程停止以後,_threadstart()函數挪用_endthread()停止線程。而且_threadstart還用__try/__except將用戶線程進口函數包起來,用於捕捉一切未處置的旌旗燈號,而且將這些旌旗燈號交給CRT處置。

所以除旌旗燈號以外,很顯著CRT包裝Windows API線程接口的最重要目標就是誰人_tiddata。這個線程公有的構造外面保留的是甚麼呢?我們可以從mtdll.h中找到它的界說,它外面保留的是諸如線程ID、線程句柄、erron、strtok()的前一次挪用地位、rand()函數的種子、異常處置等與CRT有關的並且是線程公有的信息。可見MSVC CRT並沒有應用我們後面所說的__declspec(thread)這類方法來界說線程公有變量,從而避免庫函數在多線程下掉效,而是采取在堆上請求一個_tiddata構造,把線程公有變量放在構造外部,由顯式TLS保留_tiddata的指針。

懂得了這些信息今後,我們應當會想到一個成績,那就是假如我們用CreateThread()創立一個線程然後挪用CRT的strtok()函數,按理說應當會失足,由於strtok()所須要的_tiddata其實不存在,可是我們似乎歷來沒碰著過如許的成績。檢查strtok()函數就會發明,當一開端挪用_getptd()去獲得線程的_tiddata構造時,這個函數假如發明線程沒有請求_tiddata構造,它就會請求這個構造而且擔任初始化。因而不管我們挪用哪一個函數創立線程,都可以平安挪用一切須要_tiddata的函數,由於一旦這個構造不存在,它就會被創立出來。

那末_tiddata在甚麼時刻會被釋放呢?ExitThread()確定不會,由於它基本不曉得有_tiddata如許一個構造存在,那末很顯著是_endthread()釋放的,這也恰是CRT的做法。不外我們許多時刻會發明,即便應用CreateThread()和ExitThread() (不挪用ExitThread()直接加入線程函數的後果雷同),也不會發明任何內存洩漏,這又是為何呢?經由細心檢討以後,我們發明本來暗碼在CRT DLL的進口函數DllMain中。我們曉得,當一個過程/線程開端或加入的時刻,每一個DLL的DllMain都邑被挪用一次,因而靜態鏈接版的CRT就無機會在DllMain中釋放線程的_tiddata。可是DllMain只要當CRT是靜態鏈接版的時刻才起感化,靜態鏈接CRT是沒有DllMain的!這就是形成應用CreateThread()會招致內存洩漏的一種情形,在這類情形下,_tiddata在線程停止時沒法釋放,形成了洩漏。

我們可以用上面這個小法式來測試:

#include <Windows.h>
#include <process.h>
void thread(void *a)
{
    char* r = strtok( "aaa", "b" );
    ExitThread(0); // 這個函數能否挪用都無所謂
}
int main(int argc, char* argv[])
{
    while(1) {
        CreateThread(  0, 0, (LPTHREAD_START_ROUTINE)thread, 0, 0, 0 );
        Sleep( 5 );
    }
return 0;
}

假如用靜態鏈接的CRT (/MD,/MDd)就不會有成績,然則,假如應用靜態鏈接CRT (/MT,/MTd),運轉法式後在過程治理器中不雅察它就會發明內存用量一直地上升,然則假如我們把thread()函數中的ExitThread()改成_endthread()就不會有成績,由於_endthread()會將_tiddata()釋放。

這個成績可以總結為:當應用CRT時(根本上一切的法式都應用CRT),請盡可能應用_beginthread()/_beginthreadex()/_endthread()/_endthreadex()這組函數來創立線程。在MFC中,還有一組相似的函數是AfxBeginThread()和AfxEndThread(),依據下面的道理類推,它是MFC層面的線程包裝函數,它們會保護線程與MFC相干的構造,當我們應用MFC類庫時,盡可能應用它供給的線程包裝函數以包管法式運轉准確。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved