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

来源:互联网 发布:数组是物理结构么 编辑:程序博客网 时间:2024/05/17 23:58
 

在所有的内核对象中,事件内核对象是个最基本的对象。事件能够通知一个操作已经完成。

客户机和一个服务器,它们之间需要互相进行通信例子(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函数并不非常有用(可以先不管它)。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 用移动流量很卡怎么办 淘宝店铺被恶意刷流量怎么办 一个想要公司权利的人怎么办 淘宝商家短信推广告怎么办 在群里乱发信息怎么办 支付宝扫不了码怎么办 淘宝商家收款不发货怎么办 淘宝申请退款商家拒绝怎么办 一件代发找不到供货商怎么办 淘宝京东兼职上当怎么办 退货时快递丢件怎么办 淘宝店铺代销1688有订单怎么办 供应商已解除合作无法代销怎么办 被代运营骗了怎么办 被淘宝运营骗了怎么办 淘宝运营公司骗了怎么办 被金融公司骗了怎么办 天猫品牌方投诉怎么办 淘宝卖家售假被扣了12分怎么办? 淘宝店被投诉了怎么办 淘宝商品被投诉侵权怎么办 淘宝小二胡乱判怎么办 淘宝卖家不干了怎么办 花呗剩下的钱怎么办 蚂蚁花呗无法使用怎么办 淘宝不能用花呗怎么办 淘宝号给冻结了怎么办 淘宝买家号封了怎么办 拼多多商家盗图怎么办 被拼多多盗图了怎么办 淘宝盗用图片被投诉怎么办 淘宝别人盗用我的图片怎么办 淘宝盗图申诉原图过大怎么办 淘宝别人举报我盗用图片怎么办 淘宝卖家被投诉盗图怎么办 淘宝卖家被投诉卖假货怎么办 淘宝买到假货卖家不承认怎么办 被投诉盗图扣2分怎么办 拼多多盗淘宝图怎么办 微信视频清理了怎么办 牛仔短裤买大了怎么办