2010年8月18日 星期三

利用 Event 定時處理某些事情

在軟體流程中,常常需要在固定一段時間後去做某件事,例如股市看盤軟體,每1分鐘要更新一次走勢圖,包括交易量、成交金額等資訊,初心者經常會使用 SetTimer() 來做這種事,但 SetTimer 這種事只存在於視窗中,例如 CWnd、CDialog 等等視窗。

在非 Window 的類別中做這種事可以使用 ::WaitForSingleObject() 來實作。

有個 Windows API 叫作 ::CreateEvent()

CreateEvent(lpEventAttributes, bManualReset, bInitialState, lpName);


lpEventAttributes - 屬性
需引入 SECURITY_ATTRIBUTES 結構的指標
通常為 NULL,代表此 handle 不可被繼承

bManualReset - 是否手動復原
等待接獲此事件發出的訊息後,
若為 FALSE 代表需手動復原為不發訊息,
若為 TRUE 表示等待釋放後,會自動復原為不發我息狀態

bInitialState - 初始值
以TRUE或FALSE表示初始是否發訊息

lpName - 名稱

CreateEvent 可以產生一個 Event Object
並回傳一個 HANDLE 即是這個 Event Object 的指標,
Event 有兩種狀態,發訊息與不發訊息,

可以用以下兩種改變 Event 的狀態,


::SetEvent(HANDLE); // 發訊息
::ResetEvent(HANDLE); // 不發訊息


也有其他較特殊的 API 例如

::PulseEvent(HANDLE); // 由無訊息轉換為發訊息,再瞬間轉為無訊息


在 MFC 中則是常使用包裝好的 CEvent
兩種都很方便,看心情隨意調用就行了。

範例:

若有一 Thread 必須等待某事件結束
此 Thread 即使是在執行狀態,也必須等到 g_event.SetEvent() 才會做下一行,


CEvent g_event;

UINT PowerOffThread(LPVOID pParam)
{
  ::WaitForSingleObject(g_event, INFINITE);
  // do something
}


用於定時 1 分鐘的裝置


HANDLE g_eventGetStockDetail = CreateEvent(NULL, FALSE, FALSE, NULL);

UINT PowerOffThread(LPVOID pParam)
{
  ResetEvent(g_eventGetStockDetail); // 設為不發訊息狀態
  while(::WaitForSingleObject(g_eventGetStockDetail, 60 * 1000) == WAIT_TIMEOUT)
  {
    // Get Stock Data or GetParent()->PostMessage() to do something
    if(/*記得加上停止條件,例如下午1點30分收盤*/)
    {
      break;
    }
  }
}


從外部控制停止條件的方式


HANDLE g_eventGetStock = CreateEvent(NULL, FALSE, FALSE, NULL);
UINT GetStockThread(LPVOID pParam)
{
  while(::WaitForSingleObject(g_eventGetStock, 60 * 1000) != WAIT_OBJECT_0)
  {
    // 當g_eventGetStock為無訊息時,每分鐘執行一次
    // 直到外部g_eventGetStock.SetEvent();使條件不成立即結束
  }
  return TRUE;
}


後來又想到第二個方法也可以從外部控制,只要在外部 ::SetEvent(g_eventGetStockDetail) 即可,while 內就不會 == TIMEOUT 便結束 loop 流程

真是小笨笨…

沒有留言:

張貼留言