当使用CreateProcess调用时,系统将创建一个进程和一个主线程。CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:
1在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回
2把线程退出码置为STILL_ACTIVE,把线程挂起计数置1
3分配context结构
4分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD
5lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数
6把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数
当你创建一个线程时,其实那个线程是一个循环。这样就带来了一个问题,在那个死循环里要找到合适的条件退出那个死循环,那么是怎么样实现它的呢?在Windows里往往是采用事件的方式,当然还可以采用其它的方式。在这里先介绍采用事件的方式来通知从线程运行函数退出来,它的实现原理是这样,在那个死循环里不断地使用WaitForSingleObject函数来检查事件是否满足,如果满足就退出线程,不满足就继续运行。当在线程里运行阻塞的函数时,就需要在退出线程时,先要把阻塞状态变成非阻塞状态,比如使用一个线程去接收网络数据,同时使用阻塞的SOCKET时,那么要先关闭SOCKET,再发送事件信号,才可以退出线程的。
事件对象就像一个开关:它只有两种状态---开和关。当一个事件处于”开”状态,我们称其为”有信号”否则称为”无信号”。可以在一个线程的执行函数中创建一个事件对象,然后观察它的状态,如果是”无信号”就让该线程睡眠,这样该线程占用的CPU时间就比较少。
产生事件对象的函数如下:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName );该函数创建一个Event同步对象,如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
参数说明:
lpEventAttributes // 一般为NULL
bManualReset //创建的Event是自动复位还是人工复位.如果true,人工复位, 一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复 为无信号. 如果为false,Event被设置为有信号,则当有一个wait到它的Thread时, 该Event就会自动复位,变成无信号. 如果想在每次调用WaitForSingleObject后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件的信号。
bInitialState //初始状态,true,有信号,false无信号
lpName //事件对象的名称。您在OpenEvent函数中可能使用。
补充说明:
一个Event被创建以后,可以:
用OpenEvent()API来获得它的Handle,用CloseHandle()来关闭它
用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()来使其无信号
用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号.
举例说明(一道面试题):
/***----------------Demo two----------------***/ /* 编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C, 每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推 解:依然是多线程同步的问题,A做完,B做,B做完C,依次循环,使用事件来完成。 */ #include <Windows.h> #include <iostream> using namespace std; HANDLE glo_threadEvent[3]; int glo_eventIndex = 0; DWORD WINAPI ThreadFunc(void *p) { int param = (int)p; char threadName = 'A' + param; for(int index = 0; index < 10; index++) { //线程ABC分别等待事件0,1,2 //又因为初始glo_threadEvent[0]已经被设置为有信号了,所以第一个线程首先被执行 //在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态 WaitForSingleObject(glo_threadEvent[param], INFINITE); printf("the thread is %c\n", threadName); //使其无信号 ResetEvent(glo_threadEvent[glo_eventIndex]); //设置下一个事件为有信号,则执行下一个线程 glo_eventIndex = (glo_eventIndex + 1) % 3; SetEvent(glo_threadEvent[glo_eventIndex]); } return 0; } int main(int argc, char* argv[]) { for(int index = 0; index < 3; index++) { //为每个线程句柄创建一个事件 //Creates or opens a named or unnamed event object. glo_threadEvent[index] = CreateEvent(NULL, false, false, NULL); } //首先设置第一个事件为有信号 //Sets the specified event object to the signaled state. SetEvent(glo_threadEvent[0]); HANDLE hThread[3]; for(int index = 0; index < 3; index++) { //为每个线程创建一个执行函数并带入函数参数(void *)index hThread[index] = (HANDLE)CreateThread(NULL, 0, ThreadFunc, (void *)index, 0 ,NULL); } //设置等待类型为TRUE,有信号情况下执行 WaitForMultipleObjects(3, hThread, TRUE, INFINITE); cout<<"run is end ,plz press any key to exit..."<<endl; char c = getchar(); CloseHandle(glo_threadEvent); return 0; }