根据C ++ 0x最终草案,没有办法请求线程终止。也就是说,如果需要,我们需要实施一个自己动手的解决方案。
另一方面,boost :: thread提供了一种以安全方式中断线程的机制。
在您看来,什么是最好的解决方案?设计自己的合作“中断机制”还是本土的?
答案 0 :(得分:16)
所有语言规范都表示支持不是内置于语言中。
boost::thread::interrupt
也需要线程函数的一些支持:
当被中断的线程接下来执行一个指定的中断点时(或者当执行一个时当前被阻止)
即。当线程函数没有给调用者中断的机会时,你仍然被卡住了。
我不确定你对“原住民”的意思 - 没有原生支持,除非你被boost:threads
拼写。
不过,我还是会使用一种明确的机制。无论如何你必须考虑有足够的中断点,为什么不明确它们呢?根据我的经验,额外的代码通常是微不足道的,尽管您可能需要将一些等待从单个对象更改为多个对象,这取决于您的库 - 可能看起来更加丑陋。
还可以拉出“不要使用控制流的例外”,但与线程相比,这只是一个指导原则。
答案 1 :(得分:10)
使用本机句柄取消线程是C ++中的一个错误选项,因为您需要销毁所有堆栈分配的对象。这是他们不包括取消操作的主要原因。
Boost.Thread提供了一种中断机制,需要在任何等待的原语上进行池化。由于这作为一般机制可能很昂贵,因此该标准未包括它。
您需要自己实施。请参阅我的回答here,了解如何自行实现此问题。要完成解决方案,当中断为真并且线程应该捕获此中断并完成时,应该抛出中断。
答案 2 :(得分:6)
终止线程是不安全的,因为你无法控制任何数据结构的状态,那时候正在进行。
如果要中断正在运行的线程,则必须实现自己的机制。恕我直言,如果您需要,您的设计不适合多线程。
如果您只想等待线程完成,请使用join()或future。
答案 3 :(得分:6)
预先终止线程是不安全的,因为整个过程的状态在该点之后变得不确定。该线程可能在被终止之前获得了一个关键部分。现在永远不会释放那个关键部分。堆可能会永久锁定,依此类推。
boost::thread::interrupt
解决方案的工作方式很好。它只会中断线程执行可中断的操作,比如等待Boost.Thread条件变量,或者如果线程在调用中断后执行其中一项操作。即便如此,线程并没有被毫不客气地通过绞肉机,比如Win32的TerminateThread
函数,它只会引发异常,如果你是一个表现良好的编码器并且在任何地方使用RAII,将自己清理干净并优雅地退出线程。
答案 4 :(得分:5)
实施自己动手解决方案最有意义,而且真的不应该那么难。您将需要一个同步读/写的共享变量,指示是否要求该线程终止,并且当该变量处于可以安全中断的状态时,您的线程会定期从该变量读取。如果要中断某个线程,只需同步写入该变量,然后加入该线程即可。假设它适当地协作,它应该注意到变量已被写入并关闭,导致连接函数不再阻塞。
如果你是土生土长的,你就不会得到任何东西;您只需抛弃标准和跨平台OOP线程机制的所有好处。为了使您的代码正确,线程需要合作关闭,这意味着上述通信。
答案 5 :(得分:5)
这是我对线程取消器(对于C ++ 0x)的简单实现。 我希望它会有用。
// Class cancellation_point
#include <mutex>
#include <condition_variable>
struct cancelled_error {};
class cancellation_point
{
public:
cancellation_point(): stop_(false) {}
void cancel() {
std::unique_lock<std::mutex> lock(mutex_);
stop_ = true;
cond_.notify_all();
}
template <typename P>
void wait(const P& period) {
std::unique_lock<std::mutex> lock(mutex_);
if (stop_ || cond_.wait_for(lock, period) == std::cv_status::no_timeout) {
stop_ = false;
throw cancelled_error();
}
}
private:
bool stop_;
std::mutex mutex_;
std::condition_variable cond_;
};
// Usage example
#include <thread>
#include <iostream>
class ThreadExample
{
public:
void start() {
thread_ = std::unique_ptr<std::thread>(
new std::thread(std::bind(&ThreadExample::run, this)));
}
void stop() {
cpoint_.cancel();
thread_->join();
}
private:
void run() {
std::cout << "thread started\n";
try {
while (true) {
cpoint_.wait(std::chrono::seconds(1));
}
} catch (const cancelled_error&) {
std::cout << "thread cancelled\n";
}
}
std::unique_ptr<std::thread> thread_;
cancellation_point cpoint_;
};
int main() {
ThreadExample ex;
ex.start();
ex.stop();
return 0;
}
答案 6 :(得分:4)
我的线程实现使用了pimpl习惯用法,在Impl类中,我为每个支持的OS提供了一个版本,还有一个使用boost的版本,因此我可以决定在构建项目时使用哪个版本。
我决定创建两个类:一个是Thread,它只有基本的OS提供的服务;另一个是SafeThread,它继承自Thread并具有协作中断的方法。
Thread有一个terminate()方法,可以进行侵入式终止。它是一个在SafeThread中重载的虚方法,它在其中发出事件对象的信号。有一个(静态)yeld()方法,正在运行的线程应该不时调用它;此方法检查事件对象是否已发出信号,如果是,则抛出在线程入口点的调用者处捕获的异常,从而终止该线程。当它这样做时它会发出第二个事件对象的信号,因此terminate()的调用者可以知道该线程被安全地停止了。
对于存在死锁风险的情况,SafeThread :: terminate()可以接受超时参数。如果超时到期,它会调用Thread :: terminate(),从而导致线程被侵入。当你有一些你无法控制的东西(比如第三方API),或者在死锁造成的破坏比资源泄漏等更多的情况下,这是最后的资源。
希望这对您的决定有用,并且会让您对我的设计选择有足够的了解。如果没有,我可以发布代码片段以澄清你是否愿意。
答案 7 :(得分:2)
我同意这个决定。例如,.NET允许中止任何工作线程,我从不使用此功能,也不建议对任何专业程序员执行此操作。我想决定自己,当一个工作线程可能被中断,以及这样做的方法是什么。它与硬件,I / O,UI和其他线程不同。如果线程可能在任何地方停止,这可能会导致资源管理,事务等未定义的程序行为。