Windows 程式設計/多工
當前版本的 Windows 是多工作業系統。在本章中,我們將討論一些與多工、執行緒和同步相關的工具和 API 函式,這些函式可用於 Windows。
首先,讓我們解釋一下術語。一個程序是一個單一的程式,具有單個入口點和單個出口點。一個執行緒是程序的一部分。一個程序至少有 1 個執行緒;但可以有多個執行緒。建立時,程序和執行緒會自動執行,並且由排程程式以迴圈方式分配執行時間片段。作業系統可以隨時啟用和停用任何執行緒或程序。因此,我們需要控制對程式資源的訪問,例如全域性記憶體和輸出裝置。
如果多個程序一起工作,則生成的組被稱為作業。作業也可以由 Windows 管理。
- CreateProcess 等
在處理執行緒時,會使用一些函式,例如CreateThread、ResumeThread、SuspendThread 和TerminateThread。
如果執行緒的易變性令人不安,Windows 還提供了一個名為纖程的執行物件,它只在父執行緒啟用時執行。
CreateThread 函式接受幾個引數
- 指向要線上程中執行的函式的指標。
- 指向要傳遞給執行緒函式的變數的指標。
CreateThread 函式為程序建立一個新執行緒。建立執行緒必須指定新執行緒要執行的程式碼的起始地址。通常,起始地址是程式程式碼中定義的函式的名稱。此函式接受單個引數並返回 DWORD 值。一個程序可以擁有多個執行緒同時執行相同的函式。
以下示例演示如何建立一個執行本地定義的函式 ThreadFunc 的新執行緒。
DWORD WINAPI ThreadFunc( LPVOID lpParam )
{
char szMsg[80];
wsprintf( szMsg, "ThreadFunc: Parameter = %d\n", *lpParam );
MessageBox( NULL, szMsg, "Thread created.", MB_OK );
return 0;
}
VOID main( VOID )
{
DWORD dwThreadId, dwThrdParam = 1;
HANDLE hThread;
hThread = CreateThread(
NULL, // no security attributes
0, // use default stack size
ThreadFunc, // thread function
&dwThrdParam, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier
// Check the return value for success.
if (hThread == NULL)
ErrorExit( "CreateThread failed." );
CloseHandle( hThread );
}
為簡單起見,此示例將指向 DWORD 值的指標作為引數傳遞給執行緒函式。這可以是指向任何型別的資料或結構的指標,也可以透過傳遞 NULL 指標並在 ThreadFunc 中刪除對引數的引用來完全省略。如果建立執行緒在新的執行緒退出之前退出,傳遞區域性變數的地址是有風險的,因為指標將變得無效。相反,要麼傳遞指向動態分配的記憶體的指標,要麼讓建立執行緒等待新執行緒終止。資料也可以使用全域性變數從建立執行緒傳遞到新執行緒。對於全域性變數,通常有必要透過多個執行緒同步訪問。
單個程序可以(通常)生成 2000 個執行緒。這是因為連結器分配的預設堆疊大小為每個執行緒 1MB。1MB x 2000 大約是 2GB,這是使用者程序可以訪問的最大值。以下是生成許多執行緒直到達到限制的示例程式碼
// Threads.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <process.h>
#include <conio.h>
#include <windows.h>
unsigned int __stdcall myThread (LPVOID params) {
printf ("Inside thread");
Sleep (INFINITE);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0;
unsigned int dwThreadId = 0;
while (true) {
HANDLE h = (HANDLE) _beginthreadex (NULL, 0, myThread, NULL, 0, &dwThreadId);
if (h == NULL) break;
++i;
}
printf ("\n\nI spawned %d threads", i);
Sleep (10000);
return 0;
}
執行緒的命名,一項特殊功能,僅適用於少數 Windows 偵錯程式,對於除錯執行緒非常有用,尤其是在除錯具有大量執行緒的程式時。它包括生成特殊的執行時異常 (0x406D1388),這使程式能夠將執行緒的名稱傳遞給偵錯程式。
//! NameThread - Names a threads for the debugger.
//!
//! Usage: NameThread( -1, "MyThreadIsNowNamed" );
//!
void NameThread( unsigned long a_ThreadID, char* a_ThreadName )
{
typedef struct tagTHREADNAME_INFO
{
unsigned long m_Type;// must be 0x1000
char* m_Name;// pointer to name (in user addr space)
unsigned long m_ThreadID;// thread ID (-1=caller thread)
unsigned long m_Flags;// reserved, must be zero
} THREADNAME_INFO;
THREADNAME_INFO info;
info.m_Type = 0x1000;
info.m_Name = a_ThreadName;
info.m_ThreadID = a_ThreadID;
info.m_Flags = 0;
__try
{
RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(unsigned long),
(unsigned long*) &info );
}
__except( EXCEPTION_CONTINUE_EXECUTION )
{
}
}
