How to Cancel/Detach a Future in C++11

Is there a way to cancel/detach a future in C++11?

The C++11 standard does not provide a direct way to cancel a task started with std::async. You will have to implement your own cancellation mechanism, such as passing in an atomic flag variable to the async task which is periodically checked.

Your code should not crash though. On reaching the end of main, the std::future<int> object held in result is destroyed, which will wait for the task to finish, and then discard the result, cleaning up any resources used.

cancel a c++ 11 async task

In short no.

Longer explanation: There is no safe way to cancel any threads in standard C++. This would require thread cancellation. This feature has been discussed many times during the C++11 standardisation and the general consensus is that there is no safe way to do so. To my knowledge there were three main considered ways to do thread cancellation in C++.

  1. Abort the thread. This would be rather like an emergency stop. Unfortunately it would result in no stack unwinding or destructors called. The thread could have been in any state so possibly holding mutexes, having heap allocated data which would be leaked, etc. This was clearly never going to be considered for long since it would make the entire program undefined. If you want to do this yourself however just use native_handle to do it. It will however be non-portable.

  2. Compulsory cancellation/interruption points. When a thread cancel is requested it internally sets some variable so that next time any of a predefined set of interruption points is called (such as sleep, wait, etc) it will throw some exception. This would cause the stack to unwind and cleanup can be done. Unfortunately this type of system makes it very difficult make any code exception safe since most multithreaded code can then suddenly throw. This is the model that boost.thread uses. It uses disable_interruption to work around some of the problems but it is still exceedingly difficult to get right for anything but the simplest of cases. Boost.thread uses this model but it has always been considered risky and understandably it was not accepted into the standard along with the rest.

  3. Voluntary cancellation/interruption points. ultimately this boils down to checking some condition yourself when you want to and if appropriate exiting the thread yourself in a controlled fashion. I vaguely recall some talk about adding some library features to help with this but it was never agreed upon.

I would just use a variation of 3. If you are using lambdas for instance it would be quite easy to reference an atomic "cancel" variable which you can check from time to time.

What's the C++ 11 way to fire off an asynchronous task and forget about it?

Just detach it immediately after creation.

std::thread([](){ run_async_task(); }).detach();

Once detached, the thread will no longer be joinable, so ~thread() will have no effect.
This answer discusses more details of this behavior.

As mentioned by W.B. below, std::async will not work for the following reason, pulled from this reference.

If the std::future obtained from std::async has temporary object
lifetime (not moved or bound to a variable), the destructor of the
std::future will block at the end of the full expression until the
asynchronous operation completes

How can I cancel a std::async function?

Since noone's actually answered the question yet I'll do so.

The writes and reads to the running variable are not atomic operations, so there is nothing in the code that causes any synchronisation between the two threads, so nothing ever ensures that the async thread sees that the variable has changed.

One possible way that can happen is that the compiler analyzes the code of Function, determines that there are never any writes to the variable in that thread, and as it's not an atomic object writes by other threads are not required to be visible, so it's entirely legal to rearrange the code to this:

void Function()
{
if(!running) return;
for(int i = 0; i < *input; ++i)
{
output.push_back(i);
}
}

Obviously in this code if running changes after the function has started it won't cause the loop to stop.

There are two ways the C++ standard allows you to synchronize the two threads, which is either to use a mutex and only read or write the running variable while the mutex is locked, or to make the variable an atomic variable. In your case, changing running from bool to atomic<bool> will ensure that writes to the variable are synchronized with reads from it, and the async thread will terminate.

std::future returned from std::async hangs while going out of scope

Taking from cppreference sample, only "the start", "f2 finished" and "the end" will get printed from this code (because f1 doesn't "hang"):

#include <future>
#include <thread>
#include <iostream>

int main() {
using namespace std::literals;

{
std::packaged_task<int()> task([]() {
std::this_thread::sleep_for(5s);
std::cout << "f1 finished" << std::endl;
return 42;
});
std::future<int> f1 = task.get_future();
std::thread(std::move(task)).detach();

std::future<int> f2 = std::async(std::launch::async, []() {
std::this_thread::sleep_for(3s);
std::cout << "f2 finished" << std::endl;
return 42;
});

f1.wait_for(1s);
f2.wait_for(1s);
std::cout << "the start" << std::endl;
}

// std::this_thread::sleep_for(7s);
std::cout << "the end" << std::endl;
}

For good discussion see: http://scottmeyers.blogspot.com.br/2013/03/stdfutures-from-stdasync-arent-special.html.

C++ standard library gives no support for thread kill operations.

Take care with threads you detach. Detachment per se is not "extremely bad", it may be useful in user terminable daemons for example, or if you have some other idea of orchestration and teardown. Otherwise, detach would have no point being provided by the standard library.

How to abandon or cancel std::thread

t1.swap(std::thread(&C::producer, this, 1));

The above is ill-formed, because std::thread::swap is declared:

void swap( thread& other ) noexcept;

std::thread(&C::producer, this, 1) is a temporary, and thus an rvalue. It cannot be bound to the non-const lvalue reference of swap.

Perhaps you inteded to write t1 = std::thread(&C::producer, this, 1); instead.


I don't understand why the thread does not cause an access violation

The behaviour of accessing an object outside of its lifetime is undefined. It is not guaranteed to cause an access violation.

Is this OK

No.

or is there a better way to implement something like this?

The ideal solution depends on the case.

A simple solution is to use a stateful callable to create the thread, and store a shared pointer to the shared state. It will be kept alive by whoever lives longer. This can have performance implications if the state is accessed often.

Even simpler solution is to use static storage for the shared state. This doesn't have the potential performance issues that shared pointer may have, but there are other problems with global state.



Related Topics



Leave a reply



Submit