5

I have two algorithms to solve a task X ().

How can I get a thread started for algorithm 1 and a thread started for algorithm 2 and wait for the first algorithm to finish after which I kill the other one and proceed?

I have seen that join from std::thread will make me wait for it to finish but I can't do join for both threads, otherwise I will wait for both to complete. I want to issue both of them and wait until one of them completes. What's the best way to achieve this?

Paulo Matos
  • 1,614
  • 17
  • 23
  • Probably easiest just to use a callback or function pointer i reckon. Whichever thread id calls, kill the other. – George Newton Oct 29 '14 at 09:41
  • 3
    they may have a shared `std::atomic is_finished` that they check regularly. – Jarod42 Oct 29 '14 at 09:41
  • There was a question about cancelling stared work here: http://stackoverflow.com/questions/12086622/is-there-a-way-to-cancel-detach-a-future-in-c11 – doctorlove Oct 29 '14 at 09:56

3 Answers3

3

You can't kill threads in C++11 so you need to orchestrate their demise.

This could be done by having them loop on an std::atomic<bool> variable and getting the winner to std::call_once() in order to set the return value and flag the other threads to end.

Perhaps a bit like this:

std::once_flag once; // for std::call_once()

void algorithm1(std::atomic<bool>& done, int& result)
{
    // Do some randomly timed work
    for(int i = 0; !done && i < 3; ++i) // end if done is true
        std::this_thread::sleep_for(std::chrono::seconds(std::rand() % 3));

    // Only one thread gets to leave a result
    std::call_once(once, [&]
    {
        done = true; // stop other threads
        result = 1;
    });
}

void algorithm2(std::atomic<bool>& done, int& result)
{
    // Do some randomly timed work
    for(int i = 0; !done && i < 3; ++i) // end if done is true
        std::this_thread::sleep_for(std::chrono::seconds(std::rand() % 3));

    // Only one thread gets to leave a result
    std::call_once(once, [&]
    {
        done = true; // stop other threads
        result = 2;
    });
}

int main()
{
    std::srand(std::time(0));

    std::atomic<bool> done(false);

    int result = 0;

    std::thread t1(algorithm1, std::ref(done), std::ref(result));
    std::thread t2(algorithm2, std::ref(done), std::ref(result));

    t1.join(); // this will end if t2 finishes
    t2.join();

    std::cout << "result : " << result << '\n';
}
Galik
  • 47,303
  • 4
  • 80
  • 117
  • Does this allow you to run the whole thing more than once during the lifetime of the program? The documentation doesn't mention how to reset a `std::once_flag`. – D Drmmr Oct 29 '14 at 20:13
  • @DDrmmr You couldn't run this more than once but it wouldn't be hard to rearrange things so that the once flag could be changed (pointer? or passed in as reference parameter? class member?). – Galik Oct 29 '14 at 20:23
  • What's forcing the wait on t1 to end if t2 finishes? – Paulo Matos Nov 14 '14 at 11:03
  • Oh you are checking the `done` variable in the for loop, which doesn't really work in my case because one of my algorithms providing the solution calls an external process. – Paulo Matos Nov 14 '14 at 11:07
  • @PauloJ.Matos The `done` variable forces the other thread to quit. If you are calling another process that should be fine because even though you can't kill a thread you could kill an external process. – Galik Nov 14 '14 at 13:41
2

The new C++11 standard offers some methods to solve those problems by using, e.g., futures, promises.

Please have a look at http://de.cppreference.com/w/cpp/thread/future and When is it a good idea to use std::promise over the other std::thread mechanisms?.

Community
  • 1
  • 1
sfrehse
  • 1,062
  • 9
  • 22
2

Firstly, don't kill the losing algorithm. Just let it run to completion and ignore the result.

Now, the closest thing to what you asked for is to have a mutex+condvar+result variable (or more likely two results, one for each algorithm).

Code would look something like

X result1, result2;
bool complete1 = false;
bool complete2 = false;

std::mutex result_mutex;
std::condition_variable result_cv;

// simple wrapper to signal when algoN has finished

std::thread t1([&]() { result1 = algo1();
                       std::unique_lock lock(result_mutex);
                       complete1 = true;
                       result_cv.notify_one();
                     });
std::thread t2([&]() { result2 = algo2();
                       std::unique_lock lock(result_mutex);
                       complete2 = true;
                       result_cv.notify_one();
                     });

t1.detach();
t2.detach();

// wait until one of the algos has completed
int winner;
{
  std::unique_lock lock(result_mutex);
  result_cv.wait(lock, [&]() { return complete1 || complete2; });
  if (complete1) winner=1;
  else           winner=2;
}

The other mechanisms, including the future/promise one, require the main thread to busy-wait. The only non-busy-waiting alternative is to move the post-success processing to a call_once: in this case the master thread should just join both children, and the second child will simply return when it finishes processing and realises it lost.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • "...require the main thread to busy-wait." I don't see the conceptual difference between `result_cv.wait(...)` and `result_future.get()`. – D Drmmr Oct 29 '14 at 13:19
  • If you have two results, and two futures, and you want to get whichever is available first - you have to loop calling `result_future.valid` or `.wait_for` or whatever. Otherwise, you're just blocking until the first algo is complete, which is identical to joining it. – Useless Oct 29 '14 at 14:41
  • I get what you mean now. I was thinking about using a single future for the result of either algorithm. – D Drmmr Oct 29 '14 at 19:38
  • The two threads could race to set the same promise, but I'm not sure what happens if a single promise is set twice. You could check the future state before setting the promise result, but there's a data race. – Useless Oct 30 '14 at 08:54
  • Yes, you'd need to synchronize access to the promise. – D Drmmr Oct 30 '14 at 12:32