C++ 取消运行长操作的线程
我正在努力解决我遇到的设计难题C++ 取消运行长操作的线程,c++,multithreading,oop,design-patterns,C++,Multithreading,Oop,Design Patterns,我正在努力解决我遇到的设计难题 ClassWithLongOperation { Run() { RecrusiveOperation(); } RecrusiveOperation() { /* RECURSION */ } } MyThread { ClassWithLongOperation Op1(10); Op1.Run(); // Takes several minutes.
ClassWithLongOperation
{
Run()
{
RecrusiveOperation();
}
RecrusiveOperation()
{
/* RECURSION */
}
}
MyThread
{
ClassWithLongOperation Op1(10);
Op1.Run(); // Takes several minutes.
ClassWithLongOperation Op2(20);
Op2.Run();
SomeOtherClassWithLongOperation Op3;
Op3.Run();
// Do some other stuff
}
GUI启动MyThread,运行5-6分钟。我想在我的GUI上有一个很大的取消按钮,这样用户就可以取消操作了
< >我可以创建全局布尔变量b取消,并检查它是否已设置在RecursiveOperation,但我想成为一个优秀的C++和面向对象编程人员,避免全局变量。特别是如果它们必须分布在多个文件中
那么,我如何(遵循良好的设计)安全地取消MyThread呢?我可以在设置中更改什么以允许此操作
我也在使用
\u beginthreadex
来启动线程,但是如果允许更简单的解决方案,我可以使用boost。不要使用全局变量,而是向ClassWithLongOperation和/或MyThread添加一个方法,类似于cancelOperation(),它将设置一个内部布尔变量。然后,适当的类方法需要在适当的时刻检查变量。您可以为ClassWithLongOperation实现Stop()方法,并让BigFatCancelButton的事件处理程序为当前操作调用此Stop()方法。您的标志不必是整个程序的全局标志,但它需要对类代码可见。创建标志作为私有实例成员,创建公共函数将其更改为false/true。在递归函数中,测试其值以验证任务是否应继续。如果需要,将其值设置为false(当然是通过函数)以停止递归调用,即当用户单击按钮时,您在所需实例中调用函数。这样您就不会违反任何OO原则,因为您有一个私有标志和一个公共成员函数来安全地更改它。。。。或者向Thread类中添加Stop()方法,使工作对象知道它们正在运行的线程。您还可以为工作对象引入一个Stop()方法。根据更重要的内容:停止线程或工作对象。使用全局变量实际上并不是世界上最糟糕的事情。不必要的全局变量激增会导致维护噩梦,但实际上,这听起来像是一个快速且易于理解的解决方案。但如果您想要一个干净的OO解决方案,这当然是可能的:
编辑我最初的帖子忽略了一个事实,即您希望能够按顺序运行多个操作,如果其中任何操作被取消,则不会执行其余操作。这意味着将bool
标志保留在消除器中更有用,而不是在每个可取消操作中单独使用;异常是处理实际控制流的最好方法。我还加强了一些措施(为标志本身添加了volatile
,使名称更清晰,限制了不必要的访问权限)
“双反弹”注册允许取消器授予对私有标志变量的访问权
最重要的是不要使用线程终止函数,除非是在非常特殊的情况下。为什么?他们不运行析构函数。它们也不会给目标线程任何“清理”的机会。我有点喜欢这种方法,但如果我正确理解代码,这意味着用户必须多次按“取消”来取消每个长操作。@Fiktik:不,用户只需按一次“取消”。。。你凭什么认为需要多台印刷机?(当然可能是一个bug…)好吧,我理解OP的问题的方式是,他想要一个按钮来中断他的线程应该做的所有工作。但是,对于操作链中的每个操作,您都有
cancelled
成员-这意味着用户按了CANCEL键,当前操作被中断,只是线程继续执行下一个操作。@Fiktik:您说得很对,这就从我身边消失了。我会重新考虑我的设计…这很好+1.不过,我不确定在这里使用volatile
的原因是什么。为了避免在c++11中使用UB,您通常会将其设置为原子的,在c++03中,我认为这里的volatile
没有多大区别-你害怕一些优化技术弄乱代码吗?但是我必须让ClassWithLongOperation
和SomeOtherClassWithLongOperation
的实例对GUI可见。我正试图把它们包含在线程中。@Josh:不一定。您可以为调用函数的线程创建函数,以配置实例的标志。然后,在GUI中,调用线程函数。
// A thing that can cancel another thing by setting a bool to true.
class Canceller {
public:
Canceller : cancelledFlag(false) {}
void RegisterCancellee(Cancellee const& c) {
c.RegisterCanceller(cancelledFlag);
}
void Cancel() {
cancelledFlag = true;
}
private:
volatile bool cancelledFlag;
};
class CancelButton : public Canceller {
...
// Call Cancel() from on-click event handler
...
};
class Cancellation : public std::exception {
public:
virtual const char* what() const throw() {
return "User cancelled operation";
}
};
// A thing that can be cancelled by something else.
class Cancellee {
friend class Canceller; // Give them access to RegisterCanceller()
protected:
Cancellee() : pCancelledFlag(0) {}
// Does nothing if unconnected
void CheckForCancellation() {
if (pCancelledFlag && *pCancelledFlag) throw Cancellation();
}
private:
void RegisterCanceller(volatile bool& cancelledFlag) {
pCancelledFlag = &cancelledFlag;
}
volatile bool* pCancelledFlag;
};
class Op1 : public Cancellee { // (And similarly for Op2 and Op3)
...
// Poll CheckForCancellation() inside main working loop
...
};
MyThread
{
CancelButton cancelButton("CANCEL!");
try {
ClassWithLongOperation Op1(10);
cancelButton.RegisterCancellee(Op1);
Op1.Run(); // Takes several minutes.
ClassWithLongOperation Op2(20);
cancelButton.RegisterCancellee(Op2);
Op2.Run();
SomeOtherClassWithLongOperation Op3;
cancelButton.RegisterCancellee(Op3);
Op3.Run();
} catch (Cancellation& c) {
// Maybe write to a log file
}
// Do some other stuff
}