跳轉到內容

Windows 程式設計/多工

來自 Wikibooks,開放世界中的開放書籍

程序和執行緒

[編輯 | 編輯原始碼]

當前版本的 Windows 是多工作業系統。在本章中,我們將討論一些與多工、執行緒和同步相關的工具和 API 函式,這些函式可用於 Windows。

首先,讓我們解釋一下術語。一個程序是一個單一的程式,具有單個入口點和單個出口點。一個執行緒是程序的一部分。一個程序至少有 1 個執行緒;但可以有多個執行緒。建立時,程序和執行緒會自動執行,並且由排程程式以迴圈方式分配執行時間片段。作業系統可以隨時啟用和停用任何執行緒或程序。因此,我們需要控制對程式資源的訪問,例如全域性記憶體和輸出裝置。

如果多個程序一起工作,則生成的組被稱為作業。作業也可以由 Windows 管理。

管理程序

[編輯 | 編輯原始碼]
  • CreateProcess 等

執行緒

[編輯 | 編輯原始碼]

在處理執行緒時,會使用一些函式,例如CreateThreadResumeThreadSuspendThreadTerminateThread


如果執行緒的易變性令人不安,Windows 還提供了一個名為纖程的執行物件,它只在父執行緒啟用時執行。


Clipboard

待辦事項
TerminateThread 不安全。


CreateThread

[編輯 | 編輯原始碼]

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 中刪除對引數的引用來完全省略。如果建立執行緒在新的執行緒退出之前退出,傳遞區域性變數的地址是有風險的,因為指標將變得無效。相反,要麼傳遞指向動態分配的記憶體的指標,要麼讓建立執行緒等待新執行緒終止。資料也可以使用全域性變數從建立執行緒傳遞到新執行緒。對於全域性變數,通常有必要透過多個執行緒同步訪問。

注意
使用者模式排程 (UMS) 是一種輕量級機制,應用程式可以使用它來排程自己的執行緒。僅適用於 Windows 7 和 Windows Server 2008 R2 的 64 位版本。

傳遞引數

[編輯 | 編輯原始碼]

執行緒區域性儲存 (TLS)

[編輯 | 編輯原始碼]

單個程序可以(通常)生成 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 )
  {
  }

}

下一章

[編輯 | 編輯原始碼]
華夏公益教科書