Windows 操作系统提供的函数SetEvent以及ResetEvent,用于操作事件对象。它们属于同步原语函数,用于实现线程同步和互斥

您所在的位置:网站首页 windows线程状态 Windows 操作系统提供的函数SetEvent以及ResetEvent,用于操作事件对象。它们属于同步原语函数,用于实现线程同步和互斥

Windows 操作系统提供的函数SetEvent以及ResetEvent,用于操作事件对象。它们属于同步原语函数,用于实现线程同步和互斥

2024-07-11 20:52| 来源: 网络整理| 查看: 265

文章目录 前言1、ResetEvent2、SetEvent3、WaitForSingleObject4、WaitForMultipleObjects

前言

SetEvent 和 ResetEvent 是 Windows 操作系统提供的函数,用于操作事件对象。它们属于同步原语函数,用于实现线程同步和互斥。这些函数的类型如下:

SetEvent:

声明:BOOL WINAPI SetEvent(HANDLE hEvent);功能:将指定的事件对象的状态设置为有信号状态,唤醒正在等待该事件的线程,使其可以继续执行。用途:通常用于通知等待事件的线程可以继续执行,例如在异步操作完成时通知等待线程。

ResetEvent:

声明:BOOL WINAPI ResetEvent(HANDLE hEvent);功能:将指定的事件对象的状态设置为无信号状态,使它不能唤醒等待事件的线程,等待线程将被阻塞。用途:通常用于重置事件的状态,以便稍后可以通过 SetEvent 再次将其设置为有信号状态。

这些函数通常用于多线程编程,用于协调和同步线程的操作。SetEvent 用于通知线程可以执行,而 ResetEvent 用于阻止线程执行,直到事件被设置为有信号状态。这种机制可以用于实现线程之间的通信和同步,以确保线程按照所需的顺序执行。

1、ResetEvent

代码定义:

WINBASEAPI BOOL WINAPI ResetEvent( __in HANDLE hEvent );

代码解释:

ResetEvent 是Windows API中的一个函数,用于将事件对象(Event)的状态重置为非触发状态。事件是一种用于线程同步的内核对象,它可以处于"有信号"(signaled)或"无信号"(nonsignaled)状态。通常,事件对象被用于线程之间的通信和同步。

函数原型如下:

BOOL WINAPI ResetEvent( __in HANDLE hEvent );

参数解释:

hEvent: 要重置的事件对象的句柄。

ResetEvent 函数的作用是将指定事件对象的状态从"有信号"重置为"无信号",这意味着线程等待该事件的状态将变为未满足,因此如果有线程在等待该事件,则它们将继续等待,直到该事件再次被触发。

通常情况下,ResetEvent 用于重用事件对象,例如,多个线程等待某个事件的触发,一旦事件被触发,它将执行相关操作,然后通过 ResetEvent 重新将事件状态重置为无信号,以便下一次等待。这有助于实现线程间的协同工作和同步。

请注意,ResetEvent 函数通常与 SetEvent 函数结合使用,SetEvent 用于将事件状态设置为有信号,而 ResetEvent 用于将其重置为无信号。这种组合允许多个线程在事件上等待,当事件被触发后,一个线程执行相关操作,然后通过 ResetEvent 重置事件状态,以便下一个线程等待。

结合实际代码:

int CAuthenManager::GetAccountState(const tstring& tgt) { TRACET(); if(tgt.empty()) { TRACEE("tgt is empty!"); return SDOL_ERRORCODE_FAILED; } TRACED(_T("tgt is [%s]."), tgt.c_str()); if (m_pSdoBaseHandle == NULL) { TRACEE("m_pSdoBaseHandle is NULL!"); return SDOL_ERRORCODE_FAILED; } ResetEvent(m_hEventLs); SetTimeout(GET_TICKET_TIME_OUT, SECOND_CHECK_ACCOUNT_TIME_OUT); CTimeRecorder::GetInstance()->RecordStartTime(CallInterface_LoginBySessionIDViaSSO, ::GetTickCount()); m_nLastActionId = CallInterface_LoginBySessionIDViaSSO; if(0 != SdoBase_SetSessionId(m_pSdoBaseHandle,StringHelper::UnicodeToUtf8(tgt).c_str())) { TRACEW("AM -- a.ss.l set session failed."); return FALSE; } int nError = 0; while (true) { nError = SdoBase_ExtendLoginState2(m_pSdoBaseHandle,StringHelper::UnicodeToUtf8(tgt).c_str()); if (nError == ERROR_PROCESSING) { TRACEW("AM -- g.tkt -- Sb is busy, processing another request. Error[%d]", nError); Sleep(300); continue; } break; } SetTimeout(DEFAULT_TIME_OUT, SECOND_DEFAULT_TIME_OUT); if (nError != 0) { TRACEE("AM -- g.tkt -- Calling interface failed. Error[%d]", nError); return SDOL_ERRORCODE_FAILED; } if (WAIT_TIMEOUT == WaitForSingleObject(m_hEventLs, GET_TICKET_TIME_OUT + SECOND_GET_TICKET_TIME_OUT)) { TRACEE("AM -- g.tkt -- timeout!"); return SDOL_ERRORCODE_GETTICKET_TIMEOUT; } return get_account_state_error_code; }

代码解释:

这段C++代码是一个身份认证管理器的实现,用于验证获取用户帐户的状态。以下是代码的主要步骤和逻辑:

如果传入的 tgt(目标)参数为空,会返回 SDOL_ERRORCODE_FAILED,并记录错误消息。

如果 m_pSdoBaseHandle 为 NULL,同样会返回 SDOL_ERRORCODE_FAILED,表示未初始化句柄。

调用 ResetEvent(m_hEventLs) 来重置一个事件对象,用于等待异步操作的完成。接下来,设置了一个超时时间。

调用 SdoBase_SetSessionId 来设置会话 ID,该函数传入了 tgt 的 UTF-8 表示。如果设置会话失败,会返回 FALSE。

进入一个循环,调用 SdoBase_ExtendLoginState2 来检查登录状态,传入 tgt 的 UTF-8 表示。如果返回值为 ERROR_PROCESSING,表示服务忙碌,会等待一段时间后继续尝试,直到成功为止。

设置默认超时时间,然后检查上一步的返回值,如果不为0,表示调用接口失败,返回 SDOL_ERRORCODE_FAILED。

调用 WaitForSingleObject 来等待事件 m_hEventLs 的信号,等待的超时时间是 GET_TICKET_TIME_OUT + SECOND_GET_TICKET_TIME_OUT 毫秒。 如果超时,返回 SDOL_ERRORCODE_GETTICKET_TIMEOUT,否则返回 get_account_state_error_code。

这段代码的目的似乎是执行一系列身份验证和状态检查操作,可能是为了获取用户帐户的认证状态。在代码中,使用了异步等待事件,以及处理不同的错误和超时情况。最后的返回值可能是 get_account_state_error_code 的值,但该值的定义未在提供的代码中找到。

如果没有ResetEvent(m_hEventLs) 会发生什么事情。

如果没有 ResetEvent(m_hEventLs);,事件 m_hEventLs 的状态将保持为有信号状态。这意味着一旦事件被设置为有信号状态(通常是通过 SetEvent 函数),等待事件的线程将立即被唤醒,并可以继续执行。等待事件的线程不会被阻塞,即使事件一直处于有信号状态。

在上下文中,ResetEvent 的目的是确保事件 m_hEventLs 的初始状态为无信号状态,以便等待事件的线程在异步操作开始之前被阻塞。然后,当异步操作完成时,通过 SetEvent 将事件设置为有信号状态,以唤醒等待事件的线程。

如果没有 ResetEvent(m_hEventLs);,那么等待事件的线程可能会在异步操作开始之前被唤醒,这可能导致竞争条件或不确定的行为。因此,正确地初始化事件状态是确保线程同步和协作的重要部分。

竞争条件和不确定的行为可能发生在多线程环境中,其中多个线程尝试访问和修改共享资源,但由于执行顺序的不确定性,可能导致问题。以下是一个简单的示例,说明没有适当同步的情况下可能会发生竞争条件或不确定的行为:

考虑一个具有两个线程的情况,它们都试图访问共享变量 counter:

#include #include int counter = 0; void IncrementCounter() { for (int i = 0; i m_hEventLs),事件会被触发,通知主线程或等待的线程,以便它们可以继续执行相关的逻辑。

这种异步操作的设置允许应用程序保持响应性,同时执行需要一些时间的操作,以提高用户体验。

onExtendLoginStateCallback 是一个回调函数,它的目的是处理异步操作的结果,并在需要的时候触发事件 sm_pAuthenManager->m_hEventLs 以通知等待事件的线程可以继续执行。如果没有 ::SetEvent(sm_pAuthenManager->m_hEventLs); 这段代码,等待事件的线程将不会被通知事件的触发,因此不会执行后续逻辑。

具体来说,如果没有这段代码,onExtendLoginStateCallback 回调函数将仍然执行,并会设置结果码 nResultCode,但等待事件的线程将一直等待,不会被唤醒。因此,后续逻辑不会被执行,等待事件的线程将一直阻塞在等待事件的地方,导致应用程序无法继续执行所需的操作。

所以,::SetEvent(sm_pAuthenManager->m_hEventLs); 这段代码是确保等待事件的线程能够被唤醒并执行后续逻辑的关键部分。

3、WaitForSingleObject

代码定义:

WINBASEAPI DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds );

代码解释:

WaitForSingleObject 是 Windows API 中用于等待一个内核对象(如事件、互斥、信号量等)的函数。它会阻塞当前线程,直到指定的内核对象处于有信号状态或者等待超时。

该函数的参数包括:

hHandle:要等待的内核对象的句柄,可以是事件、互斥、信号量等。dwMilliseconds:等待的超时时间(以毫秒为单位)。如果为零,表示立即返回,不等待;如果为 INFINITE,表示永久等待,直到对象变为有信号状态。

WaitForSingleObject 的返回值包括:

WAIT_OBJECT_0:对象变为有信号状态。WAIT_ABANDONED:对象为互斥对象,且之前拥有该互斥对象的线程意外终止,导致互斥对象的计数不再准确。通常情 况下,应用程序需要谨慎处理此返回值。WAIT_TIMEOUT:等待超时,对象未变为有信号状态。WAIT_FAILED:等待失败,可以调用 GetLastError 函数获取错误代码。

WaitForSingleObject 是多线程编程中用于线程同步的重要函数之一。它允许一个线程等待另一个线程通知某个事件的发生或者等待某个资源的可用性。通过合理使用超时参数和返回值,可以实现不同的等待策略,例如等待直到对象变为有信号状态、等待一段时间后超时返回、等待一段时间后轮询等。

举个例子:

if (WAIT_TIMEOUT == WaitForSingleObject(m_hEventLs, GET_TICKET_TIME_OUT + SECOND_GET_TICKET_TIME_OUT)) { TRACEE("AM -- g.tkt -- timeout!"); return SDOL_ERRORCODE_GETTICKET_TIMEOUT; } return get_account_state_error_code;

代码解释:

这段代码的逻辑是首先等待事件对象 m_hEventLs,如果等待超时(即事件未在规定时间内触发),就会执行以下逻辑:

打印错误消息,使用 TRACEE 函数输出 “AM – g.tkt – timeout!”,表示发生了等待超时。

返回错误代码 SDOL_ERRORCODE_GETTICKET_TIMEOUT,表示等待超时错误。

如果事件在规定时间内触发,WaitForSingleObject 不返回 WAIT_TIMEOUT,那么代码会跳过上述两个步骤,直接执行 return get_account_state_error_code;,返回 get_account_state_error_code,这个值可能是在 onExtendLoginStateCallback 回调函数中设置的。

这段代码的作用是等待事件,如果事件超时未触发,则返回一个特定的错误代码,否则返回 get_account_state_error_code 的值,这个值可能在事件触发后的回调函数中设置。

4、WaitForMultipleObjects

代码定义:

WINBASEAPI DWORD WINAPI WaitForMultipleObjects( __in DWORD nCount, __in_ecount(nCount) CONST HANDLE *lpHandles, __in BOOL bWaitAll, __in DWORD dwMilliseconds );

代码解释:

WaitForMultipleObjects 也是 Windows API 中用于等待多个内核对象的函数,它允许一个线程等待多个对象中的任何一个或全部。具体而言,它等待一个对象数组中的多个内核对象之一或所有内核对象都变为有信号状态。

该函数的参数包括:

nCount:要等待的内核对象句柄数组中的对象数目。lpHandles:一个包含要等待的内核对象句柄的数组。bWaitAll:一个布尔值,如果为 TRUE,则只有当数组中的所有对象都变为有信号状态时,函数才会返回;如果为 FALSE,则只要数组中的任何一个对象变为有信号状态,函数就会返回。dwMilliseconds:等待的超时时间(以毫秒为单位)。如果为零,表示立即返回,不等待;如果为 INFINITE,表示永久等待,直到对象变为有信号状态。

WaitForMultipleObjects 的返回值包括:

WAIT_OBJECT_0 到 WAIT_OBJECT_0 + nCount - 1:对象数组中的某个对象变为有信号状态。如果 bWaitAll 为 FALSE,则返回的值表示数组中的哪个对象发生了变化;如果 bWaitAll 为 TRUE,则返回 WAIT_OBJECT_0。WAIT_TIMEOUT:等待超时,对象数组中的任何一个对象都未变为有信号状态。WAIT_FAILED:等待失败,可以调用 GetLastError 函数获取错误代码。

通过合理使用参数,可以实现多种等待策略,如等待任何一个对象变为有信号状态、等待所有对象都变为有信号状态、等待一段时间后超时返回等。这个函数在多线程编程和处理多个并发事件时非常有用。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3