windows多线程编程简介(2)

当使用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;
}


你可能感兴趣的:(thread,线程,对象,C语言,event)