所以我有一个multithreading的C ++控制台应用程序,我想在其中处理控制台closures事件以执行清理。
我有这样的效果:
bool running = true; ServerSocket* server; std::mutex mutex; BOOL WINAPI HandlerRoutine(DWORD) { running = false; server->shutdown(); std::lock_guard<std::mutex> guard(mutex); return TRUE; } int main() { std::lock_guard<std::mutex> guard(mutex); SetConsoleCtrlHandler(&HandlerRoutine, TRUE); try { ServerSocket server(27015); ::server = &server; while (running) { TCPSocket* client = server.accept(true); } } catch (const ServerSocket::ServerShutdownException&) { return 0; } }
如果我从HandlerRoutine
返回,我的程序会被HandlerRoutine
地终止,所以我必须等待main()
结束。
然而,在主要结束后,我得到一个exception,告诉我一个互斥体在繁忙时被破坏,从dynamic atexit destructor for 'mutex'()
抛出。 这使我相信,一旦main
返回,静态variables和全局variables就会被销毁,而我的处理函数就会被无效的全局variables所笼罩。
这是标准指定的行为,如果是这样,关于如何能达到我想要的效果的任何想法?
在这种情况下,我只是泄漏mutex
对象。 你不想在最后一个线程终止之前调用析构函数,在最后一个线程终止的时候没有必要调用它。
std::mutex& mutex = *new mutex; // freed by OS at process exit
你可以尝试boost :: application 。
这里的例子是wait_for_termination_request.cpp
是的,你的推论是正确的。 似乎最好的选择是注销你的处理程序,然后等待它完成之前从main()返回。 但是,如果不是出于任何原因的选项,你可以做的其他事情是将所有的全局变量包装在一个结构中:
struct Globals { bool running; serverSocket* server; std::mutex mutex; };
有一个单一的,全局的shared_ptr
到该结构的一个实例:
std::shared_ptr<Globals> globals = std::make_shared<Globals>();
在处理程序中创建shared_ptr
的副本:
BOOL WINAPI HandlerRoutine(DWORD) { std::shared_ptr<Globals> myGlobals = globals; ... }
并且只能依赖于处理程序中的myGlobals
(不能保证globals
指针本身在线程的整个生命周期内都保持有效)。 这样一切都保持活着,直到每个人都完成了。
当然,这假定当HandlerRoutine
开始时globals
仍然有效。 如果情况并非如此(即如果系统可以在主返回之后但在流程结束之前调用处理程序),那么我将删除这个答案。
我很想和互斥玩乒乓球。 没有一个,而是两个互斥体。
第一个是由mymain
(基本上是你的main
副本)。 main
做什么,只是叫我的mymain
。
第二个是由HandlerRoutine
持有的,从mymain
返回后由main
获得。
如果你在没有调用HandlerRoutine
情况下关闭,你只需HandlerRoutine
main的结尾跳下来。
如果在HandlerRoutine
被调用之后关闭,则其上的main
块将完成。
简单地计划泄漏mutex
是不够的,就好像在main
已经计划关闭期间调用HandlerRoutine
,它的server->shutdown
可能正在访问无效内存。
为了处理竞争条件(被调用 – 或到达锁定 – 在main
已经退出之后,该进程正在清除全局变量?),需要完成第二个mutax( HandlerRoutine
访问)的一些工作。 将HandlerRoutine
互斥量存储在指针中,并使用无锁技术非常小心地访问它,可能涉及自旋锁。
为了扩大提及互斥是不必要的评论,这是一个选择:
BOOL WINAPI HandlerRoutine(DWORD) { running = false; server->shutdown(); Sleep(INFINITE); return TRUE; // just to stop the compiler complaining }