简体   繁体   中英

How to cancel std::async when condition is met?

I am running an asynchronous task and want to cancel it when a certain condition ( bool ) is met.

void MyClass::createTask()
{
    this->future = std::async(std::launch::async, [this](){
        while(this->CONDITION == false)
        {
            // do work
        }
    });
}

void MyClass::cancelTask()
{
    this->CONDITION = true;
    this->future.get();
}

Obviously, calling MyClass::cancelTask() would cause a data-race, because this->CONDITION is being written to and read from at the same time. So the first thing that came to my mind is to use a std::mutex . However that would mean that the task has to lock and unlock the mutex on every new iteration of the while-loop. Since the async task is performance critical, this seems like a bad choice.

Is there a cleaner, and especially a more perfomant way to achieve what I am trying to do? Switching from std::async to std::thread would be ok if it enabled an efficient solution.

As far as I know there is no elegant way to close a thread/async task in C++.

A simple way is to use std::atomic<bool> or std::atomic_flag instead of a mutex.

If you are familiar with boost library, than you could use boost::thread with interruption_points.

I have a solution for this kind of requeirements. I use std::mutex , std::condition_variable and std::unique_lock<std::mutex> to create tow methods: pauseThread and resumeThread .

The idea is use the condition_variable and unique_lock to make the thread wait for a time, for example 5 seconds, and after the time os over the thread continue its execution. But, if you want to interrupt the condition_variable you could use its method notify_one() .

Using your code, and continue with your idea, i made some changes to your class:

MODIFICATION: I modify the flag bKeepRunning.

MyClass.h

#include <mutex>
#include <chrono>
#include <future>
#include <atomic>

class MyClass
{
    std::atomic<bool> bKeepRunning;

    std::mutex mtx_t;
    std::condition_variable cv_t;
    std::future<void> _future;

public:
    MyClass();
    ~MyClass();

    void createTask();
    void stopTask();

    void pauseThread(int time);
    void resumeThread();

}

MyClass.cpp

#include "MyClass.h"

#include <iostream>

using namespace std;

MyClass::MyClass()
{
    bKeepRunning = false;
}

MyClass::~MyClass()
{
}

void MyClass::createTask()
{
    bKeepRunning = true;
    _future = std::async(std::launch::async, [this]() {
        int counter = 0;
        cout << "Thread running" << endl;
        while (bKeepRunning)
        {
            counter++;
            cout << "Asynchronous thread counter = [" << counter << "]" << endl;
            this->pauseThread(5);//Wait for 5 seconds
        }
        cout << "Thread finished." << endl;
    });
}

void MyClass::stopTask()
{
    cout << "Stoping Thread." << endl;
    bKeepRunning = false;
    resumeThread();
}

void MyClass::pauseThread(int time)
{
    std::unique_lock<std::mutex> lck_t(mtx_t);
    cv_t.wait_for(lck_t, chrono::seconds(time));
}

void MyClass::resumeThread()
{
    cout << "Resumming thread" << endl;
    cv_t.notify_one();
}

I made a console sample to show how it works:

Main.cpp

#include <iostream>
#include <sstream>
#include <string>

#include "MyClass.h"

using namespace std;

int main(int argc, char* argv[])
{
    MyClass app;
    char line[80];

    cout << "Press Enter to stop thread." << endl;
    app.createTask();

    cin.getline(line,80);
    app.stopTask();
}

If you need some other period of time to pause your thread, you can try to change the interval and time of chrono::seconds(time) to, for example, chrono::milliseconds(time) that is using milliseconds.+

At the end, if you execute this sample, you could get an output like:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM