線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。一個進程可以擁有多個線程,但是一個線程必須有一個進程。線程自己不擁有系統資源,只有運行所必須的一些數據結構,但它可以與同屬于一個進程的其它線程共享進程所擁有的全部資源,同一個進程中的多個線程可以并發執行。
在C/C++中可以通過CreateThread函數在進程中創建線程,函數的具體格式如下:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadID);
參數的含義如下:
lpThreadAttrivutes:指向SECURITY_ATTRIBUTES的指針,用于定義新線程的安全屬性,一般設置成NULL;
dwStackSize:分配以字節數表示的線程堆棧的大小,默認值是0;
lpStartAddress:指向一個線程函數地址。每個線程都有自己的線程函數,線程函數是線程具體的執行代碼;
lpParameter:傳遞給線程函數的參數;
dwCreationFlags:表示創建線程的運行狀態,其中CREATE_SUSPEND表示掛起當前創建的線程,而0表示立即執行當前創建的進程;
lpThreadID:返回新創建的線程的ID編號;
如果函數調用成功,則返回新線程的句柄,調用WaitForSingleObject函數等待所創建線程的運行結束。函數的格式如下:
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
參數的含義如下:
hHandle:指定對象或時間的句柄;
dwMilliseconds:等待時間,以毫秒為單位,當超過等待時間時,此函數返回。如果參數設置為0,則該函數立即返回;如果設置成INFINITE,則該函數直到有信號才返回。
一般情況下需要創建多個線程來提高程序的執行效率,但是多個線程同時運行的時候可能調用線程函數,在多個線程同時對一個內存地址進行寫入操作,由于CPU時間調度的問題,寫入的數據會被多次覆蓋,所以要使線程同步。
就是說,當有一個線程對文件進行操作時,其它線程只能等待。可以通過臨界區對象實現線程同步。臨界區對象是定義在數據段中的一個CRITICAL_SECTION結構,Windows內部使用這個結構記錄一些信息,確保同一時間只有一個線程訪問改數據段中的數據。
使用臨界區的步驟如下:
(1)初始化一個CRITICAL_SECTION結構;在使用臨界區對象之前,需要定義全局CRITICAL_SECTION變量,在調用CreateThread函數前調用InitializeCriticalSection函數初始化臨界區對象;
(2)申請進入一個臨界區;在線程函數中要對保護的數據進行操作前,可以通過調用EnterCriticalSection函數申請進入臨界區。由于同一時間內只能有一個線程進入臨界區,所以在申請的時候如果有一個線程已經進入臨界區,則該函數就會一直等到那個線程執行完臨界區代碼;
(3)離開臨界區;當執行完臨界區代碼后,需要調用LeaveCriticalSection函數離開臨界區;
(4)刪除臨界區;當不需要臨界區時調用DeleteCriticalSection函數將臨界區對象刪除;
下面的代碼創建了5個線程,每個線程在文件中寫入10000個“hello”:
#include <stdio.h>
#include <windows.h>
HANDLE hFile;
CRITICAL_SECTION cs;//定義臨界區全局變量
//線程函數:在文件中寫入10000個hello
DWORD WINAPI Thread(LPVOID lpParam)
{
int n = (int)lpParam;
DWORD dwWrite;
for (int i = 0;i < 10000;i++)
{
//進入臨界區
EnterCriticalSection(&cs);
char data[512] = "hello\r\n";
//寫文件
WriteFile(hFile, data, strlen(data), &dwWrite, NULL);
//離開臨界區
LeaveCriticalSection(&cs);
}
printf("Thread #%d returned successfully\n", n);
return 0;
}
int main()
{
char *filename = "hack.txt";
WCHAR name[20] = { 0 };
MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename) + 1, name, sizeof(name) / sizeof(name[0]));
//創建文件
hFile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error.\n");
return 0;
}
DWORD ThreadID;
HANDLE hThread[5];
//初始化臨界區
InitializeCriticalSection(&cs);
for (int i = 0;i < 5;i++)
{
//創建線程,并調用Thread寫文件
hThread[i] = CreateThread(NULL, 0, Thread, (LPVOID)(i + 1), 0, &ThreadID);
printf("Thread #%d has been created successfully.\n", i + 1);
}
//等待所有進程結束
WaitForMultipleObjects(5, hThread, TRUE, INFINITE);
//刪除臨界區
DeleteCriticalSection(&cs);
//關閉文件句柄
CloseHandle(hFile);
return 0;
}
結果如圖: