C++多線程筆記1 學會使用四種方式創建c++線程

C++多線程筆記1 學會使用四種方式創建c++線程

1.線程和進程概念關係:

進程是一組離散的(執行)程序任務集合;

線程是進程上下文中執行的代碼序列;

兩者之間具體關係:

線程是進程的可執行單元,是計算機分配CPU機時的基本單元。一個進程可以包含一個或多個線程,進程是通過線程去執行代碼的。同一個進程的多個線程共享該進程的資源和操作系統分配給該進程的內存空間。每個進程必須有一個主線程,主線程退出之後該進程也就退出了。一個進程的主線程是由系統創建的。

在單CPU中,表面上看好像是多個進程中的多個線程共同執行,實際上是操作系統根據調度規則、依次的 將一個一個的線程可執行代碼加載進CPU中執行的;即,CPU在同一時刻只能執行一段代碼,由於CPU的頻率非常快,迅速的在各個線程間進行切換,所以給人的感覺就好像是多個線程共同執行。

在多核CPU的電腦中,確實是多個線程共同執行,因爲多核處理器中,每個核擁有自己的緩存、寄存器和運算器。

2.C++創建新線程的四種方式:

方式一(不推薦):CreateThread(記得關閉線程句柄)

CreateThread是一種微軟在Windows API中提供了建立新的線程的函數,該函數在主線程的基礎上創建一個新線程。線程終止運行後,線程對象仍然在系統中,必須通過CloseHandle函數來關閉該線程對象。


HANDLE WINAPI CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 線程安全屬性,在Windows NT中,NULL使用默認安全性,不可以被子線程繼承,否則需要定義一個結構體將它的bInheritHandle成員初始化爲TRUE。因此一般使用NULL
  SIZE_T dwStackSize,  // 線程堆棧大小 設置初始棧的大小,以字節爲單位,如果爲0,那麼默認將使用與調用該函數的線程相同的棧空間大小。任何情況下,Windows根據需要動態延長堆棧的大小
  LPTHREAD_START_ROUTINE lpStartAddress, // 線程函數地址 指向線程函數的指針,形式:@函數名,函數名稱沒有限制,
  LPVOID lpParameter, // 線程函數參數 是一個指向結構的指針,不需傳遞參數時,爲NULL
  DWORD dwCreationFlags,  // 指定線程是否立即啓動 一般取值爲0(立即激活)或者爲)CREATE_SUSPENDED(0x00000004)掛起線程
  LPDWORD lpThreadId    // 存儲線程ID號
);

返回值:函數成功,返回線程句柄;函數失敗返回false。若不想返回線程ID,設置值爲NULL。

函數聲明方式
DWORD WINAPI 函數名 (LPVOID lpParam); //標準格式

DWORD WINAPI 函數名 (LPVOID lpParam)
{
    return 0;
}
CreateThread(NULL, 0, 函數名, 0, 0, 0);

void 函數名(); 也就是普通的沒有標準的函數聲明方式

需要注意:使用void 函數名()此種線程聲明方式時,lpStartAddress需要加入LPTHREAD_START_ROUTINE轉換,也就是創建線程時第三個參數要寫成(LPTHREAD_START_ROUTINE)函數名

創建方式的缺點:使用這種方法可能會引發內存泄漏問題,不推薦,推薦使用方式三

詳細瞭解CreateThread參考鏈接:

百度百科:https://baike.baidu.com/item/CreateThread

msdn: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread

方式二(簡單):_beginthread()


unsigned long _beginthread(
  void(_cdecl *start_address)(void *), //聲明爲void (*start_address) (void *)形式
  unsigned stack_size, //是線程堆棧大小,一般默認爲0
  void *arglist //向線程傳遞的參數,一般爲結構體
);

_Beginthread函數創建一個在start_address開始執行例程的線程。 當線程從該例程返回時,就會自動終止。使用此函數必須包含頭文件 #include <process.h>

這個函數看起來過於簡單,他無法做到線程的掛起狀態。另外重要的一點是,它所產生出來的線程所做的第一件事情就是關閉掉handle,所以由它所返回的handle可能可用,也可能不可用

方式三(推薦,安全度高):_beginthreadex(需要關閉線程句柄)

uintptr_t _beginthreadex( // NATIVE CODE
   void *security,
   unsigned stack_size,
   unsigned ( __stdcall *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr
);

需要使用頭文件#include <process.h> 需要的設置:ProjectàSetting–>C/C+±->User run-time library 選擇Debug Multithreaded 或者Multithreaded。即使用: MT或MTD。退出時需要使用_endthreadex(handle);

關於方式二和方式三的區別和使用可以參考下面鏈接

msdn:https://docs.microsoft.com/zh-cn/cpp/c-runtime-library/reference/beginthread-beginthreadex?view=vs-2019

方式四:AfxBeginThread(會自動釋放的,不用你去釋放)

AfxBeginThread一個計算機函數,功能是創建用戶界面線程和工作者線程。 常見用法是AfxBeginThread(ThreadProc,this)。比較常見的就是在MFC裏面使用。兩者的區別在於用戶頁面能夠處理消息響應,而後者工作者線程則不能

主要介紹工作者線程:

CWinThread* AFXAPI AfxBeginThread(
AFX_THREADPROC pfnThreadProc,// 線程的入口函數,聲明一定要如下: UINT MyThreadFunction( LPVOID pParam );
LPVOID pParam,//傳遞入線程的參數,注意它的類型爲:LPVOID,所以我們可以傳遞一個結構體入線程.
int nPriority,//優先級
UINT nStackSize,//堆棧大小
DWORD dwCreateFlags,//創建標識,是否掛起
LPSECURITY_ATTRIBUTES lpSecurityAttrs//安全屬性
)

一個是線程函數的指針,一個是傳遞給這個函數的參數。實際中我們經常這樣用 AfxBeginThread(ThreadProc,this);//把this傳過去,就可以調用類的成員了. 這樣線程函數就可以使用和操作類的成員了。千萬要注意線程函數是靜態類函數成員。當然也可以把this換成別的結構體指針,效果一樣。

需要注意的是,聲明必須是這種方式

UINT MyThreadProc( LPVOID pParam )
{
    return 0;
}

瞭解界面線程及AfxBeginThread更多使用參看鏈接:

https://www.cnblogs.com/lujin49/p/4557655.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章