正在取消线程池中运行的任意作业

Cancelling arbitary jobs running in a thread_pool

本文关键字:运行 任意 作业 取消 线程      更新时间:2023-10-16

线程池有办法取消正在进行的任务吗?更好的是,有没有一种安全的替代方案可以按需取消thread_pools中的不透明函数调用?

终止整个进程是个坏主意,使用本机句柄执行pthread_cancel或类似的API只是最后的手段。

额外

如果取消是立即的,则奖金是可以接受的,但如果取消有一些时间限制"保证"(例如,在有问题的线程的0.1执行秒内取消)

更多详细信息

我不限于使用Boost.Thread.thread_pool或任何特定的库。唯一的限制是与C++14的兼容性,以及至少在BSD和基于Linux的操作系统上工作的能力。

任务通常与数据处理相关,使用C-API(extern "C")动态预编译和加载,因此是不透明的实体。其目的是执行计算密集型任务,并可在用户发送中断时选择取消这些任务。

启动时,特定任务的thread_id是已知的,因此如果需要,可以起诉某些API来查找更多详细信息。

免责声明

我知道不建议使用本机线程句柄来取消/退出线程,这是糟糕设计的标志。我也不能使用boost::this_thread::interrupt_point修改函数,但如果有帮助的话,可以将它们封装在lambdas/其他构造中。我觉得这是一个非常困难的情况,所以欢迎其他建议,但它们需要在现有功能中具有最小的侵入性,并且对于正在讨论的功能集来说,它们的范围可能非常大。

编辑:

澄清

我想这应该放在"更多细节"部分,但我希望它保持独立,以表明现有的2个答案是基于有限的信息。看完答案后,我回到绘图板上,提出了以下"限制条件",因为我提出的问题过于笼统。如果我要发布一个新问题,请告诉我。

我的接口承诺了一个"const"输入(函数式编程风格的非可变输入),根据需要使用互斥/按值复制,并通过const&传递(并期望线程表现良好)。

我还错误地使用了"任意"一词,因为工作不是任意的(从经验上讲),并且有以下限制:

  • 一些从"互联网"下载的文件已经使用了"条件变量">
  • 不违反常量正确性
  • 可以生成其他线程,但它们不能超过父线程
  • 可以使用互斥,但不能存在于函数体之外
  • 输出通过作为参数传递的atomic<shared_ptr>
  • 纯函数(没有与外部共享的状态)**
  • **可以是绑定函子的lambda,在这种情况下,函数需要确保其数据结构没有损坏(通常情况下,状态为1或2atomic<inbuilt-type>)。通常从外部数据库查询内部状态(类似于cookie+web服务器的架构,选项卡/浏览器可以随时关闭)

这些约束并不是作为合同或任何东西写下来的,而是我根据当前使用的"模块"进行了概括。这些工作在做什么方面是任意的:GPU/CPU/互联网都是公平的。

由于大量使用库,插入定期检查是不可行的。这些库(不归我们所有)并没有被设计为定期检查条件变量,因为在一般情况下,这会导致性能损失,并且重写库是不可能的。

线程池有没有办法取消正在进行的任务?

不在一般性级别,不,如果线程中运行的任务是在C或C++中本地任意实现的,也不在。在未终止整个线程的情况下,不能在任务完成前终止正在运行的任务,除非与任务合作。

更好然而,有没有一种安全的替代方案可以按需取消不透明thread_pools中的函数调用?

否。获得(近似)特定线程的按需抢占的唯一方法是通过pthread_kill()向其传递信号(即不阻塞或忽略)。如果这样的信号终止了线程而不是整个进程,那么它不会自动为释放分配的对象或管理互斥对象或其他同步对象的状态做出任何规定。如果信号没有终止线程,那么中断可能会在不适合这种信号使用的代码中产生令人惊讶和不想要的效果。

终止整个过程是个坏主意,使用本机句柄执行pthread_cancel或类似的API只是最后的手段。

请注意,pthread_cancel()可以被线程阻止,即使不被阻止,其效果也可能无限期延迟。当效果确实发生时,它们不一定包括内存或同步对象清理。你需要线程配合它自己的取消来实现这些。

线程与取消的合作在一定程度上取决于您选择的取消机制的细节。

只有在组件与系统其他部分有有限、受约束、受管理的交互时,才有可能取消非合作、非设计取消的组件:

  • 组件拥有的资源应该由外部管理(系统知道哪个组件使用什么资源)
  • 所有访问都应该是间接的
  • 共享资源的修改在完成之前应该是安全和可逆的

这将允许系统清理资源、停止操作、取消未完成的更改。。。

这些房产都不便宜线程的所有属性都与这些属性完全相反

线程只有一个隐含的所有权概念,在运行的线程中是显而易见的:对于一个已删除的线程,确定该线程拥有什么是不可能的。

线程直接访问共享对象。线程可以启动对共享对象的修改;取消后,如果在操作过程中停止,这种修改将是部分的、无效的、不连贯的。

被取消的线程可能会留下被锁定的互斥对象试图访问共享对象的其他线程至少对这些互斥对象的后续访问将死锁

或者他们可能会发现某些数据结构处于错误状态。

为任意非协作线程提供安全的取消是不可行的,即使对线程同步对象进行了非常大规模的更改。甚至不需要对线程基元进行完全的重新设计。

您必须使线程几乎像完整的进程一样才能做到这一点;但是那就不叫线程了