您的位置:首页 > 编程语言

window核心编程-内核对象线程同步

2013-06-04 00:26 435 查看

window核心编程-内核对象线程同步

1、内核对象线程同步介绍

上一章讲了关键字(临界区)线程同步,使用关键字线程同步,我们很容易陷入死锁的情形,这是因为我们无法为进入关键段指定一个最长等待时间。

本章将讨论如何使用内核对象来对线程同步。我们也将看到,与用户模式下的同步机制(关键段同步)相比,内核对象的用途要广泛的多。实际上,内核对象唯一的缺点就是它们的性能。内核对象包括进程、线程以及作业,几乎所有这些内核对象都可以用来进行同步。对线程同步来书,这些内核对象中的每一种要么处于触发状态,要么处于未触发状态。Microsoft为每种对象创建了一些规则,规定如何在这两种状态之间进行转换。例如,进程内核对象在创建的时候总是处于未触发状态。当进程终止的时候,操作系统会自动使进程内核对象变成触发状态。当进程内核对象被触发后,它将永远保持这种状态,再也不会变回到未触发状态。在进程内核对象的内部有一个布尔变量,当系统创建内核对象的时候会把这个变量的值初始化为false(未触发)。当进程终止的时候,操作系统会自动把相应的内核对象中的这个布尔值设为true,表示该对象已经被触发。

下边讲一些内核同步中用到的函数。

等该函数使一个线程自愿进入等待状态,直到指定的内核对象被触发为止。

DWORD waitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

//hObject:内核对象句柄

//dwMilliseconds等待时间ms为单位,INFINITE为一直等待,只到内核对象被触发为止。

DWORD WaitForMultipleObjects( DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );

//waitForSingleObject和WaitForMultipleObjects相似,唯一的不同之处在于它允许调用线程同时检查多个内核对象的触发状态

/*函数的返回值告诉调用方函数为什么它得以继续运行。如果给bWaitAll传的是FALSE,那么只要任何一个对象被触发,函数就会立即返回。这时的返回值是WAIT_OBJECT_0和(WAIT_OBJECT_0+dwCount-1)之间的任何一个值。换句话说,如果返回值既不是WAIT_TIMEOUT,也不是WAIT_FAILED,那么我们应该把返回值减去WAIT_OBJECT_0。得到的数值是我们在第二个参数中传的句柄数组的一个索引,用来告诉我们被触发的是那个对象。*/

//下面的事例代码可以更清晰的对此进行解释

HANDLE h[3];//我的博客:http://blog.csdn.net/windows_nt
h[0] = hProcess1;
h[1] = hProcess2;
h[2] = hProcess3;
DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000);
switch(dw)
{
case WAIT_FAILED:
	//Bad call to function (invalid handle)
	break;
case WAIT_TIMEOUT:
	//None of the objects became signaled within 5000 milliseconds
	break;
case WAIT_OBJECT_0 + 0:
	//h[0] signaled, hProcess1 terminated
	break;
case WAIT_OBJECT_0 + 1:
	//h[1] signaled, hProcess2 terminated
	break;
case WAIT_OBJECT_0 + 2:
	//h[2] signaled, hProcess3 terminated
	break;
}


2、事件内核对象

//事件包含一个使用计数(这一点和所有其他内核对象一样),一个用来表示事件是自动重置事件还是手动重置事件的布尔值,以及另一个用来表示事件有没有被触发的布尔值。

//有两种不同类型的事件对象:手动重置事件和自动重置事件。当一个手动重置事件被触发的时候,正在等待该事件的所有线程都将变成可调度状态,而当一个自动重置事件被触发的时候,只有一个正在等待该事件的线程会变成可调用状态。

//创建一个事件内核对象

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性

BOOL bManualReset, //自动重置/手动重置

BOOL bInitialState, //是否触发

LPCSTR lpName );//事件内核对象的名字,可以为空

//新版本

HANDLE CreateEventEx(

LPSECURITY_ATTRIBUTES psa, //安全属性

PCTSTR pszName, //名字

DWORD dwFlags, //是否触发

DWORD dwDesiredAccess)

//dwDesiredAccess:允许我们指定在创建事件时返回的句柄对事件有何种访问权限。这是一种创建事件句柄的新方法,它可以减少权限,相比较而言,CreateEvent()总是被授予全部权限。但CreateEventEx()更有用的地方在于它允许我们以减少权限的方式来打开一个已经存在的事件,而CreateEvent()总是要求全部权限。

//下边这个例子展示了如何使用事件内核对象来对线程进行同步。

//Create a global handle to a manual-reset, nonsignaled event.
HANDLE g_hEvent;
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
					 LPSTR lpCmdLine, int nShowCmd )
{
	//Create the manual-reset, nonsignaled event
	g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	//Spawn 3 new threads
	HANDLE hThread[3];
	DWORD dwThread;
	hThread[0] = _beginthread(NULL, 0, wordCount, NULL, 0, &dwThread);
	hThread[1] = _beginthread(NULL, 0, SpellCheck, NULL, 0, &dwThread);
	hThread[2] = _beginthread(NULL, 0, GrammarCheck, NULL, 0, &dwThread);

	OpenFileAndReadContentsIntoMemory(...);
	//allow all 3 threads to access the memory
	SetEvent(g_hEvent);
}

DWORD WINAPI wordCount(PVOID pvParam)
{
	//Wait until the file's data is in memory
	waitForSingleObject(g_hEvent, INFINITE);
	//access the memory block.
	return 0;
}

DWORD WINAPI SpellCheck(PVOID pvParam)
{
	//Wait until the file's data is in memory
	waitForSingleObject(g_hEvent, INFINITE);
	//access the memory block.
	return 0;
}

DWORD WINAPI GrammarCheck(PVOID pvParam)
{
	//Wait until the file's data is in memory
	waitForSingleObject(g_hEvent, INFINITE);
	//access the memory block.
	return 0;
}


下边是我自己写的一个事例片段,很简单

CString strName = _T("");

UINT CBcgTestDlg::ThreadWorkFunc(LPVOID lPvoid)
{
	
	for (int n = 0; n < 10000; n++)
	{
		strName = _T("http://blog.csdn.net/windows_nt");
		strName = strName + _T("\n");
		TRACE(strName);
	}
	
	return 0;
}

void CBcgTestDlg::OnOK() 
{
	CWinThread* pThread = NULL;

	for (int n = 0; n < 10000; ++n)
	{
		if (pThread)
		{
			WaitForSingleObject(pThread->m_hThread, INFINITE);
			delete pThread;
		}
		
		pThread = AfxBeginThread(ThreadWorkFunc, NULL, 0, CREATE_SUSPENDED, NULL);
		if (pThread)
		{
			pThread->m_bAutoDelete = FALSE;
			pThread->ResumeThread();
		}
	}
}
注意线程函数可以为类函数,但必须是静态函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: