您的位置:首页 > 其它

windows笔记-【内核对象线程同步】事件内核对象

2010-12-07 16:14 211 查看
在所有的内核对象中,事件内核对象是个最基本的对象。事件能够通知一个操作已经完成。

客户机和一个服务器,它们之间需要互相进行通信例子(vs2008 )

事件内核对象的组成


一个使用计数
(与所有内核对象
一样),

一个用于指明该事件是个自动重置
的事件还是一个人工重置
的事件的布尔值,

一个用于指明该事件处于已通知状态
还是未通知状态
的布尔值。

有两种不同类型的事件对象


一种
是人工重置的事件
,另一种
是自动重置的事件


当人工重置的事件得到通知时


等待该事件的所有线程均


变为可调度线程。

当自动重置的事件得到通知时,

等待该事件的线程中只有一个线程

变为可调度线程。

当一个线程执行初始化操作,然后通知另一个线程执行剩余的操作时
,事件使用得最多。事件初始化为未通知状态,然后,当该线程完成它的初始化操作后,它就将事件设置为已通知状态。这时,一直在等待该事件的另一个线程发现该事件已经得到通知,因此它就变成可调度线程。这第二个线程知道第一个线程已经完成了它的操作。



CreateEvent

函数创建事件内核对象


HANDLE CreateEvent
(

PSECURITY_ATTRIBUTES psa,

BOOL fManualReset,

BOOL fInitialState,

PCTSTR pszName

);

内核对象的操作技巧见前面相关章节,比如,如何设置它们的安全性
,如何进行使用计数
如何继承它们的句柄
如何按名字共享对象等


fMannualReset
参数
是个布尔值,它能够告诉系统是创建一个人工重置的事件(
TRUE
)还是创建一个自动重置的事件(
FALSE
)。

fInitialState
参数
用于指明该事件是要初始化为已通知状态(
TRUE
)还是未通知状态(
FALSE
)。当系统创建事件对象后,
createEvent
就将与进程相关的句柄
返回给事件
对象。

其他进程中的线程可以获得对该对象的访问权
,方法是使用在
pszName
参数中传递的相同值(见:命名对象
),使用继承性
使用

DuplicateHandle

函数
等来调用
CreateEvent
,或者调用
OpenEvent ,

pszName
参数中设定一个与调用
CreateEvent
时设定的名字相匹配的名字:

HANDLE OpenEvent
(

DWORD fdwAccess,

BOOL fInherit,

PCTSTR pszName

);

与所有情况中一样,当不再需要事件内核对象时,应该调用
CloseHandle
函数。

一旦事件已经创建,就可以直接控制它的状态


当调用
SetEvent
时,可以将事件改为
已通知状态


BOOL SetEvent

(
HANDLE hEvent);

当调用
ResetEvent
函数时,可以将该事件改为
未通知状态


BOOL ResetEvent

(
HANDLE hEvent);

事件成功等待的副作用


自动重置的事件
:自动重置到未通知状态
(所以通常没有必要为自动重置的事件调用
ResetEvent
函数)。所以他的名字才有自动两字。

人工重置的事件:
Microsoft
没有

为人工重置的事件
定义成功等待的副作用。所以它才叫人工。

简单例子,如何使用事件内核对象对线程进行同步


这个例子清楚地展示出使用人工重置事件
与自动重置事件
之间的差别。

// Create
a global handle to a manual-reset, nonsignaled event.

HANDLE g_hEvent;

int

WINAPI WinMain(...)

{

// Create
the manual-reset, nonsignaled event.

g_hEvent = CreateEvent
(NULL, TRUE, FALSE, NULL);
//
人工

// Spawn 3 new threads.

HANDLE hThread[
3];

DWORD dwThreadID;

hThread[
0] = _beginthreadex(NULL, 0, WordCount, NULL, 0, &dwThreadID);

hThread[
1] = _beginthreadex(NULL, 0, SpellCheck, NULL, 0, &dwThreadID);

hThread[
2] = _beginthreadex(NULL, 0, GrammarCheck, NULL, 0, &dwThreadID);

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);

}

当这个进程启动时,它创建一个人工重置的未通知状态的事件,并且将句柄保存在一个全局变量中。这使得该进程中的其他线程能够非常容易地访问同一个事件对象
。现在
3
个线程已经产生。这些线程要等待文件的内容读入内存,然后每个线程都要访问它的数据。一个线程进行单词计数,另一个线程运行拼写检查器,第三个线程运行语法检查器。这
3
个线程函数的代码的开始部分都相同,每个函数都调用
WaitForSingleObject
,这将使线程暂停运行,直到文件的内容由主线程读入内存为止。

一旦主线程将数据准备好,它就调用
SetEvent
,给事件
发出通知信号。这时,系统就使所有这
3
个辅助线程进入可调度状态,它们都获得了
CPU
时间,并且可以访问内存块。注意,这

3
个线程都以
只读
方式访问内存。这就是所有
3
个线程能够同时运行的
唯一原因

还要注意,如果计算机上配有多个
CPU
,那么所有
3
个线程都能够真正地同时运行,从而可以在很短的时间内完成大量的操作。

如果这里使用自动重置的事件而不是人工重置的事件
,那么应用程序的行为特性就有很大的差别。当主线程调用
SetEvent
之后,系统只允许一个辅助线程变成可调度状态
。同样,也无法保证系统将使哪个线程变为可调度状态。其余两个辅助线程将继续等待。

已经变为可调度状态的线程拥有对内存块的独占访问权。让我们重新编写线程的函数,使得每个函数在返回前调用
SetEvent
函数(就像
WinMain
函数所做的那样)。这些线程函数现在变成下面的形式:

DWORD WINAPI WordCount(
PVOID pvParam)

{

//Wait until the file's data is in memory.

WaitForSingleObject
(
g_hEvent, INFINITE);

//Access the memory block.

...

SetEvent
(
g_hEvent);

return(
0);

}

DWORD WINAPI SpellCheck(
PVOID pvParam)

{

//Wait until the file's data is in memory.

WaitForSingleObject
(
g_hEvent, INFINITE);

//Access the memory block.

...

SetEvent
(
g_hEvent);

return(
0);

}

DWORD WINAPI GrammarCheck(
PVOID pvParam)

{

//Wait until the file's data is in memory.

WaitForSingleObject
(
g_hEvent, INFINITE);

//Access the memory block.

...

SetEvent
(
g_hEvent);

return(
0);

}

当线程完成它对数据的专门传递时,它就调用
SetEvent
函数,该函数允许系统使得两个正在等待的线程中的一个成为可调度线程。同样,我们不知道系统将选择哪个线程作为可调度线程,但是该线程将进行它自己的对内存块的专门传递。当该线程完成操作时,它也将调用
SetEvent
函数,使第三个即最后一个线程进行它自己的对内存块的传递。注意,当使用自动重置事件时,如果每个辅助线程均
以读
/
写方式访问内存块,那么就不会产生任何问题,这些线程将不再被要求将数据视为只读数据


BOOL PulseEvent
(
HANDLE hEvent);

PulseEvent
函数使得事件变为已通知状态,然后立即又变为未通知状态
,这就像在调用
SetEvent
后又立即调用
ResetEvent
函数一样。如果在人工重置的事件上调用
PulseEvent
函数,那么在发出该事件时,等待该事件的任何一个线程或所有线程将变为可调度线程。如果在自动重置事件上调用
PulseEvent
函数,那么只有一个等待该事件的线程变为可调度线程。如果在发出事件时没有任何线程在等待该事件,那么将不起任何作用。

PulseEvent
函数并不非常有用(可以先不管它)。

本文地址:/article/5259752.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: