跳转到内容

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 )
  {
  }

}

下一章

[编辑 | 编辑源代码]
华夏公益教科书