7

我有一个 C++ Win32 应用程序,当用户想要关闭应用程序时,它有许多线程可能正忙于执行 IO(HTTP 调用等)。目前,我玩得很好,等待所有线程结束后再从main. 有时,这需要的时间比我想要的要长,事实上,当我可以退出时让用户等待似乎毫无意义。但是,如果我继续从 中返回main,我可能会崩溃,因为析构函数开始被调用,而仍然有线程在使用这些对象。

所以,认识到在一个理想的、柏拉图式的美德世界中,最好的办法是等待所有线程退出然后干净地关闭,下一个最好的现实世界解决方案是什么?简单地使线程退出更快可能不是一种选择。目标是尽快让进程停止运行,例如,可以在其上安装新版本。我正在做的唯一磁盘 IO 是在事务数据库中,所以我并不十分担心拔掉插头。

4

11 回答 11

7

使用重叠 IO,这样您就可以始终控制处理您的 I/O 的线程,并且可以随时停止它们;您可以让他们在 IOCP 上等待并可以向其发布应用程序级别的关闭代码,或者您可以等待 OVERLAPPED 结构中的事件并等待“所有线程请立即关闭”事件。

总之,避免阻止您无法取消的呼叫。

如果你不能并且你被困在一个阻塞的套接字调用中,那么你总是可以从已经决定是时候关闭的线程中关闭套接字,并让正在执行 IO 的线程总是检查'shutdown now ' 重试之前的事件...

于 2008-10-16T19:42:26.753 回答
5

我使用了一种基于异常的技术,该技术在许多 Win32 应用程序中都非常有效。

为了终止一个线程,我使用QueueUserAPC()对一个引发异常的函数的调用进行排队。但是,抛出的异常不是从“异常”类型派生的,因此只会被我线程的包装程序捕获。

这样做的好处如下:

  • 线程中不需要特殊代码来使其“可停止” - 一旦它进入可警报等待状态,它将运行 APC 函数。
  • 当异常在堆栈中运行时,所有析构函数都会被调用,因此您的线程干净地退出。

您需要注意的事项:

  • 做任何事catch (...)都会吃掉你的例外。用户代码应始终使用catch(const Exception &e)或类似!
  • 确保您的 I/O 和延迟以“可提醒”的方式完成。例如,这意味着调用sleepex(N, true)而不是sleep(N).
  • CPU 绑定线程需要sleepex(0,true)偶尔调用以检查是否终止。

您还可以“保护”代码区域以防止任务在关键部分终止。

于 2008-10-16T21:30:52.633 回答
2

最好的方法:在应用程序运行时做你的工作,在关闭时什么都不做(或接近)(也适用于启动)。如果您坚持这种模式,那么您可以在关闭请求到来时立即拆除线程(而不是“友好地”处理它),而不必担心仍然需要完成的工作。

在您的特定情况下,如果您在那里进行本地工作,您可能需要等待 IO 完成(至少写入)。HTTP 请求等你可能会直接放弃/关闭(同样,除非你正在写东西)。但是,如果您在此关闭期间编写并等待,那么您可能希望将此通知用户,而不是让您的进程在结束时看起来挂起。

于 2008-10-16T15:42:42.697 回答
2

我建议让您的 GUI 和工作在不同的线程上完成。当用户请求关闭时,立即关闭 GUI,以显示应用程序已关闭。允许工作线程在后台优雅地关闭。

于 2008-10-16T15:56:35.803 回答
0

无论你做什么,都不要使用 TerminateThread,尤其是在任何可能在 OS HTTP 调用中的东西上。您可能会破坏 IE,直到重新启动。

将所有 IO 更改为异步或非阻塞模型,以便它们可以监视终止事件。

于 2008-10-16T21:09:48.917 回答
0

如果你想乱七八糟地拔掉插头,exit(0) 就可以了。

于 2008-10-16T15:51:39.840 回答
0

如果您需要突然关闭:只需调用 ExitProcess - 无论如何,只要您从 WinMain 返回就会调用它。Windows 本身会创建许多无法清理的工作线程 - 它们会因进程关闭而终止。

如果您有任何线程正在执行某种类型的写入 - 显然这些线程需要有机会关闭它们的资源。但除此之外 - 忽略边界检查器警告,只需将地毯从他们脚下拉出来。

于 2008-10-17T11:35:55.173 回答
0

您可以调用 TerminateProcess - 这将立即停止该过程,无需通知任何人,也无需等待任何事情。

于 2008-10-17T12:03:01.033 回答
0

我曾经遇到过类似的问题,尽管在 Visual Basic 6 中:来自应用程序的线程将连接到不同的服务器,下载一些数据,执行一些循环操作该数据,并将结果存储在中央服务器上。

然后,新的要求是线程应该可以从主窗体停止。我以一种简单但肮脏的方式实现了这一点,方法是让线程在 N 个循环后停止(大约相当于半秒)以尝试打开具有特定名称的互斥锁。成功后,他们立即停止正在做的任何事情并退出,否则继续。

该互斥锁仅由主窗体创建,一旦创建,所有线程很快就会自行关闭。缺点是用户需要手动指定它想要再次运行线程 - 另一个“启用线程运行”按钮通过释放互斥锁来实现这一点:D

保证这个技巧适用于互斥操作是原子的。问题是您永远无法确定线程是否真正关闭 - 处理“openMutex 成功”情况的逻辑失败可能意味着它永远不会结束。您也不知道何时/是否所有线程都已关闭(假设您的代码是正确的,这将与循环停止和“收听”所需的时间大致相同)。

使用 VB 的多线程“公寓”模型,从线程到主应用程序来回发送信息有点困难,“触发后忘记”或仅从主应用程序发送到线程要容易得多。因此,需要这种长切。使用 C++,您可以自由使用多线程模型,因此这些约束可能不适用于您。

于 2008-10-16T20:27:40.983 回答
0

*NULL = 0是最快的方法。如果您不想崩溃,请致电exit()或其 win32 等效项。

于 2011-12-09T02:32:29.687 回答
-5

指示用户拔下计算机。除此之外,您必须放弃异步活动。还是那是HWIND?我永远记不起C++。当然,您可以采取中间道路并在文本文件或注册键中快速记下放弃了哪些操作,以便下次程序运行时它可以再次自动执行该操作或询问用户是否愿意这样做。根据您放弃异步操作时丢失的数据,您可能无法做到这一点。如果您正在与用户交互,您可能需要考虑一个对话框或一些 UI 交互来解释为什么需要这么长时间。

就个人而言,我更喜欢用户的指令,即拔掉电脑。:)

于 2008-10-16T15:50:09.093 回答