等待main()返回?

时间:2014-05-12 14:48:32

标签: c++ windows multithreading visual-c++ c++11

所以我有一个多线程C ++控制台应用程序,我想在其中处理控制台关闭事件以执行清理。

我有一些这样的效果:

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返回,我的程序会毫不客气地终止,所以我必须等待main()结束。

然而,在主要结束后,我得到一个例外,告诉我一个互斥体在忙碌时被摧毁,从dynamic atexit destructor for 'mutex'()抛出。这让我相信一旦main返回就会破坏静态变量和全局变量,使我的处理函数与无效的全局变量挂钩。

这是标准的指定行为吗?如果是,是否有任何关于如何达到预期效果的想法?

5 个答案:

答案 0 :(得分:4)

在这种情况下,我只会泄漏mutex对象。您不希望在终止最后一个线程之前调用析构函数,并且在最后一个线程终止期间调用它是没有意义的。

std::mutex& mutex = *new mutex; // freed by OS at process exit

答案 1 :(得分:1)

您可以尝试boost::application

这里是示例wait_for_termination_request.cpp

答案 2 :(得分:0)

是的,您的扣除是正确的。似乎最好的选择是取消注册您的处理程序,然后等待它从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指针本身在线程的整个生命周期内保持有效)。这样一切都会保持活着,直到每个人都完成它。

当然,假设globals开始时HandlerRoutine仍然有效。如果情况并非如此(例如,如果系统可以在主要退货之后但在流程结束之前调用处理程序),那么我将删除此答案。

答案 3 :(得分:0)

我很想用互斥体打乒乓球。没有一个,而是两个互斥体。

第一个是mymain(基本上是main的副本)。 main只会致电mymain

第二个由HandlerRoutine保留,并在main返回后由mymain获取。

如果你在没有调用HandlerRoutine的情况下关闭,你就会掉落主要的结尾。

如果您在调用HandlerRoutine后关闭,则其上的main阻止完成。

只是计划泄漏mutex是不够的,就好像在HandlerRoutine已经计划关闭期间调用main一样,其server->shutdown可能无法访问存储器中。

需要做一些关于第二个mutax(HandlerRoutine访问)的工作来处理竞争条件(在main已经退出之后被调用 - 或者到达锁定,并且进程正在清理全局变量?)。将HandlerRoutine互斥锁存储在指针中,并使用无锁技术非常小心地访问它,可能涉及旋转锁。

答案 4 :(得分:0)

为了扩展提及不需要互斥锁的注释,这是一个替代方案:

BOOL WINAPI HandlerRoutine(DWORD)
{
  running = false;
  server->shutdown();
  Sleep(INFINITE);
  return TRUE; // just to stop the compiler complaining
}