简体   繁体   English

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

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

I'm porting some code that uses native MS API and I've implemented something that tries to mimic event handling with CreateEvent , SetEvent , WaitForSingleObject , WaitForMultipleObjects , etc... 我正在移植一些使用本机MS API的代码,并且实现了一些尝试模仿事件处理的事件,例如CreateEventSetEventWaitForSingleObjectWaitForMultipleObjects等。

I've used std::condition_variable as in my case it only needs to work in the same application for thread synchronization. 我使用了std::condition_variable因为在我的情况下,它只需要在同一应用程序中工作即可进行线程同步。

In my test app everything seems to be working as it should but when I try the same code in my production code calls to wait_for break the code although no exceptions occur. 在我的测试应用程序中,一切似乎都可以正常工作,但是当我在生产代码中尝试相同的代码时,调用wait_for中断代码,尽管不会发生异常。 It just seems to carry on when I am stepping through it and then it never times out. 当我逐步执行它时,它似乎一直在进行,然后永不超时。 I have all exception turned on in the debug configuration and nothing is signaled. 我在调试配置中打开了所有异常,但未发出任何信号。 I have no idea what is causing this... 我不知道是什么原因造成的...

I am testing this in VS2013 in a Win7 machine 我正在VS2013的Win7机器中对此进行测试

The code is divided into two classes Handler and Event , where Handler holds a fixed array of Event . 该代码分为HandlerEvent两个类,其中Handler持有一个固定的Event数组。 Event::WaitForSingleObject is where I actually wait on the condition_variable and it can either be called directly or called from Handler::WaitForMultipleObjects with a minimal timeout so that all events in a list are verified. Event::WaitForSingleObject实际上是我在condition_variable上等待的地方,可以直接调用它,也可以从Handler::WaitForMultipleObjects调用它,并且超时最小,以便验证列表中的所有事件。

I have multiple threads accessing the Handler object but the array elements can't be accessed at the same time (this is by design), and as such access to the array isn't protected. 我有多个线程访问Handler对象,但是不能同时访问数组元素(这是设计使然),因此对数组的访问不受保护。

Here is the the header (concurrency2.h) file that implements the code that tries to handle event handling: 以下是标头(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 

And here is main.cpp 这是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; } 

Any help would be appreciated! 任何帮助,将不胜感激!

Cheers, Andre 干杯,安德烈

I'm not sure if this is the cause however, I think the following line: 我不确定这是否是原因,但我认为以下内容:

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

Shouldn't use the std::try_to_lock parameter as it doesn't block to acquire the lock -- although I don't see where in your example this could fail... if for example more than one thread was calling WaitForSingleObject on the same Event instance, the lk might not acquire the mutex and the following std::wait_for could exhibit "undefined behaviour". 不应该使用std::try_to_lock参数,因为它不会阻塞获取锁-尽管我看不到您的示例中的哪个位置可能会失败...例如,如果有多个线程正在调用上的WaitForSingleObject ,同样的Event例如, lk可能无法获得互斥体及以下std::wait_for可能表现出“未定义行为”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM