Cancel a thread performing a long operation

I am trying to develop the constructive predicament that I have.

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 } 

The GUI launches MyThread, which runs for 5-6 minutes. I want me to have a large Undo button on my GUI, so the user can cancel the operation.

I could create a bCancelled global boolean and check if it was set in RecursiveOperation, but I want to be a good C ++ and OO programmer and avoid global variables. Especially if they have to distribute several files.

So how do I (after a good design) safely cancel MyThread? What can I change in the settings to allow this?

I also use _beginthreadex to start the stream, but I could use boost if this made the solution easier.

+6
source share
5 answers

Your flag should not be global for your entire program, but it should be visible for your class code. Create a flag as a private member of the instance and a public function to change it to false / true. In your recursive function, check its value to see if the task should continue. If you want, set its value to false (through the function, of course) to stop recursive calls, that is, when the user clicks the button, you call the function in the desired instance. This way you will not violate the OO principle, since you have a closed flag and an open member function to safely change it.

+4
source

Using a global variable is actually not the worst in the world. Spreading unnecessary global variables leads to service nightmares, but in reality it seems like a quick and easy solution. But if you want a clean OO solution, this is certainly possible:

EDIT My initial message does not take into account the fact that you want to perform several operations in sequence, and if any of them is canceled, none of the remaining operations are performed. This means that it is more useful to store the bool flag inside the compensator, rather than separately in each undo operation; and exceptions are the finest way to handle the actual control flow. I also tightened up a few things (added volatile for the flag itself, simplified names, limited unnecessary access rights).

 // 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 } 

Registering a "double bounce" allows the recipient to allow access to the private flag variable.

Most importantly, do not use thread completion functions, except in very specialized cases. What for? They do not control destructors. They also do not give the target the chance to "clear."

+3
source

Instead of using a global variable, add a method to ClassWithLongOperation and / or MyThread, something like cancelOperation (), which will set the internal boolean. The appropriate class methods will then need to check the variable at the appropriate times.

+1
source

You can implement the Stop () method for your ClassWithLongOperation class and an event handler for BigFatCancelButton to call this Stop () method for the current operation.

+1
source

... Or add the Stop () method to the Thread class and make work objects aware of the threads in which they are running. You can also use the Stop () method for work objects. Depending on which is more important: stop the thread or work object.

0
source

Source: https://habr.com/ru/post/924735/


All Articles