簡體   English   中英

跨平台事件處理-std :: condition_variable wait_for似乎忽略了超時

[英]Cross platform Event handling - std::condition_variable wait_for seems ignores timeout

我正在移植一些使用本機MS API的代碼,並且實現了一些嘗試模仿事件處理的事件,例如CreateEventSetEventWaitForSingleObjectWaitForMultipleObjects等。

我使用了std::condition_variable因為在我的情況下,它只需要在同一應用程序中工作即可進行線程同步。

在我的測試應用程序中,一切似乎都可以正常工作,但是當我在生產代碼中嘗試相同的代碼時,調用wait_for中斷代碼,盡管不會發生異常。 當我逐步執行它時,它似乎一直在進行,然后永不超時。 我在調試配置中打開了所有異常,但未發出任何信號。 我不知道是什么原因造成的...

我正在VS2013的Win7機器中對此進行測試

該代碼分為HandlerEvent兩個類,其中Handler持有一個固定的Event數組。 Event::WaitForSingleObject實際上是我在condition_variable上等待的地方,可以直接調用它,也可以從Handler::WaitForMultipleObjects調用它,並且超時最小,以便驗證列表中的所有事件。

我有多個線程訪問Handler對象,但是不能同時訪問數組元素(這是設計使然),因此對數組的訪問不受保護。

以下是標頭(concurrency2.h)文件,該文件實現了嘗試處理事件處理的代碼:

 #ifndef AAAAAA #define AAAAAA #include<thread> #include<future> #include<mutex> #include<array> static const unsigned k_INFINITE = 0xFFFFFFFF; namespace cross { class Event; using EventId = int; //prefer alias declaration to typedefs typedef Event* Event_handle; class Event { std::string m_name; //notification agent for event notification (this is what other threads will use to notify the event) std::mutex m_cvmutex; std::condition_variable m_cv; bool m_signaled; bool m_waiting; //notification agents for event termination (used internally to force an event to be signaled so we can kill the event) std::mutex m_tcvmutex; std::condition_variable m_tcv; bool m_tcv_signaled; bool m_terminating; Event(void) {} int SignalTermination() { m_tcv.notify_all(); m_tcv_signaled = true; return -2; } void WaitForTermination() { if (!m_tcv_signaled) { std::unique_lock<std::mutex> lk(m_tcvmutex); m_tcv.wait(lk); //wait for ever lk.unlock(); } } public: Event(std::string _name) : m_name(_name), m_signaled(false), m_waiting(false), m_terminating(false), m_tcv_signaled(false) {} void SetEvent(bool kill=false) { m_cv.notify_all(); m_signaled = true; m_terminating = kill; std::cout << name() << " notify... " << std::endl; if (m_terminating) { WaitForTermination(); } } void ResetEvent() { m_signaled = false; } bool IsWaiting() { return m_waiting; } const char* name() { return m_name.c_str(); } /* returns: 0 if event was triggered -1 if timeout has occured -2 if I'm trying to get rid of this */ int WaitForSingleObject(unsigned timeout = k_INFINITE) { if (m_terminating) { return SignalTermination(); } int ret = -1; if (m_signaled) { ret = 0; //m_set_before = false; } else { m_waiting = true; std::unique_lock<std::mutex> lk(m_cvmutex, std::try_to_lock); //std::cout << "wait... " ; if (timeout == k_INFINITE) { m_cv.wait(lk); ret = 0; } else { auto wait = m_cv.wait_for(lk, std::chrono::milliseconds(timeout)); if (wait == std::cv_status::timeout) { ret = -1; } else if (wait == std::cv_status::no_timeout) { ret = 0; } } lk.unlock(); m_waiting = false; } if (m_terminating) { ret = SignalTermination(); } return ret; } }; class Handler { public: Handler() : count(0), wait_for_multiple_objects_timeout(100) { m_events.fill(nullptr); } ~Handler() { //for (const auto& ev : m_events) for (auto ev : m_events) { delete ev; //ev = NULL; this doesn't work inside a range based loop } } EventId CreateEvent(char* name) { m_events[count] = new Event(name); return count++; //the event id will be the index in the events vector. TODO This needs to be smarter! } void SetEvent(EventId eid) { if (eid < count && m_events[eid] != nullptr) { m_events[eid]->SetEvent(); } } void ResetEvent(EventId eid) { if (eid < count && m_events[eid] != nullptr) { m_events[eid]->ResetEvent(); } } void ResetEvents() { for (auto ev : m_events) { if (ev != nullptr) ev->ResetEvent(); } } bool CloseHandle(EventId eid) { if (eid < count && m_events[eid] != nullptr) { if (m_events[eid]->IsWaiting()) { //if it's waiting, set the event and signal it to be killed. //because we are trying to kill the even, SetEvent will block until the event is dead m_events[eid]->SetEvent(true); } delete m_events[eid]; m_events[eid] = nullptr; return true; } return false; } void CloseAllHandles() { for (int i = 0; i < 100; ++i) { if (m_events[i] != nullptr) { CloseHandle(i); } } } int WaitForSingleObject(EventId eid, unsigned timeout = k_INFINITE) { if (eid < count && m_events[eid] != nullptr) { return m_events[eid]->WaitForSingleObject(timeout); } return -1; } EventId WaitForMultipleObjects(unsigned count, EventId events[], bool wait_for_all=false, unsigned timeout = k_INFINITE) { /* timeout is the value that we must wait until at least one (or all if wait_for_all is true), of the events is triggered. Each event will wait for 100 miliseconds in order to allow all events to be verified. timeout is used for each of the events in the list and is not a global value. */ std::vector<unsigned> timeouts; std::vector<bool> signaled; for (unsigned i = 0; i < count; ++i) { timeouts.push_back(0); signaled.push_back(false); } bool wait_for_ever = timeout == k_INFINITE; do { for (unsigned i = 0; i < count; ++i) { EventId result = m_events[events[i]]->WaitForSingleObject(wait_for_multiple_objects_timeout); if (result == -2) { return -2; } else if (result == -1) { //timeout waiting for events[i] if (!wait_for_ever) { timeouts[i] += wait_for_multiple_objects_timeout; if (timeouts[i] >= timeout) { //as soon as one of the events timeout, they are all timedout. This can be different if we need it to... return -1; } } } else { signaled[i] = true; if (!wait_for_all) return events[i]; bool all_signaled = true; for (auto sig : signaled) { if (sig == false) { all_signaled = false; break; } } if (all_signaled) { return events[i]; //return last signaled event when all events are signaled } } } } while (1); return -1; } const char* GetName(EventId eid) { if (eid < count && m_events[eid] != nullptr) { return m_events[eid]->name(); } return ""; } private: int count; std::array<Event_handle, 100> m_events; unsigned wait_for_multiple_objects_timeout; }; } //end namespace cross #endif 

這是main.cpp

 // BackToTheFuture.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include<thread> #include<iostream> #include<mutex> #include <string> #include <sstream> #include "concurrency2.h" using namespace std; cross::Handler handler; cross::EventId events[6]; void mythread() { int roll_index = 0; do { try { cross::EventId signaled = handler.WaitForMultipleObjects(3, events, true);// false, 5000); if (signaled >= 0) { cout << handler.GetName(signaled) << " signaled@1" << endl; break; } else if (signaled == -1) { cout << handler.GetName(roll_index) << " time out@1" << endl; break; } else if (signaled == -2) break; } catch (...) //need to handle this properly, for now will do { cout << "exception..." << endl; } if (++roll_index > 2) roll_index = 0; } while (1); cout << "EXITED thread 1\\n"; } void mythread2() { int roll_index = 0; do { try { //WaitForMultipleObjects test - pass an array of EventIds cross::EventId signaled = handler.WaitForMultipleObjects(3, events+3, true);// , false, 5000); if (signaled >= 0) { cout << handler.GetName(signaled) << " signaled@2" << endl; break; } else if (signaled == -1) { cout << handler.GetName(roll_index) << " time out@2" << endl; break; } else if (signaled == -2) break; } catch (...) //need to handle this properly, for now will do { cout << "exception..." << endl; } if (++roll_index > 2) roll_index = 0; } while (1); cout << "EXITED thread 2\\n"; } int _tmain(int argc, _TCHAR* argv[]) { events[0] = handler.CreateEvent("event 0"); events[1] = handler.CreateEvent("event 1"); events[2] = handler.CreateEvent("event 2"); events[3] = handler.CreateEvent("event a"); events[4] = handler.CreateEvent("event b"); events[5] = handler.CreateEvent("event c"); std::thread t1(mythread); std::thread t2(mythread2); string input = ""; int myNumber = 0; bool _exit = false; do { getline(cin, input); // This code converts from string to number safely. stringstream myStream(input); if (myStream >> myNumber) { if (myNumber > 5) { handler.CloseAllHandles(); _exit = true; handler.SetEvent(0); } else { handler.SetEvent(myNumber); } } } while (!_exit); t1.join(); t2.join(); cout << "Terminated... press any key to continue..." << endl; getline(cin, input); return 0; } 

任何幫助,將不勝感激!

干杯,安德烈

我不確定這是否是原因,但我認為以下內容:

std::unique_lock<std::mutex> lk(m_cvmutex, std::try_to_lock);

不應該使用std::try_to_lock參數,因為它不會阻塞獲取鎖-盡管我看不到您的示例中的哪個位置可能會失敗...例如,如果有多個線程正在調用上的WaitForSingleObject ,同樣的Event例如, lk可能無法獲得互斥體及以下std::wait_for可能表現出“未定義行為”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM