16

For example, I have two threads, t1 and t2. I want to wait for t1 or t2 to finish. Is this possible?

If I have a series of threads, say, a std::vector<std::thread>, how can I do it?

Felix Glas
  • 15,065
  • 7
  • 53
  • 82
t123yh
  • 657
  • 2
  • 7
  • 18
  • 1
    Do you have a [mcve] demonstrating your attempt to solve this yourself? – Xirema Apr 25 '17 at 15:15
  • 1
    I don't know of any standard mechanism that does this directly. What problem are you actually trying to solve? There is perhaps an alternative solution. – François Andrieux Apr 25 '17 at 15:19
  • 1
    @KerrekSB that will block for all of the threads. I think if you want to block for _one_ thread (out of n) sticking with `std::thread` you will need an external variable (like `std::atomic_bool`) as a signal. – Chad Apr 25 '17 at 15:20
  • Ah, right, you want to block only until the first thread completes... never mind! – Kerrek SB Apr 25 '17 at 15:21
  • @t123yh maybe POSIX ? – Bob Apr 25 '17 at 15:27
  • It's a question of what you've written and what you want to do next. You could have them all share a mutex and wait on that in the main thread. This depends on what you want to do with the threads that don't finish first (let them finish? Kill them? Ignore them?) . Do they have output? How are you getting that? What about the output from the not-first threads? Etc, etc... – Donnie Apr 25 '17 at 18:11

4 Answers4

10

There's always wait & notify using std::condition_variable, e.g.:

std::mutex m;
std::condition_variable cond;
std::atomic<std::thread::id> val;

auto task = [&] {
    std::this_thread::sleep_for(1s); // Some work

    val = std::this_thread::get_id();
    cond.notify_all();
};

std::thread{task}.detach();
std::thread{task}.detach();
std::thread{task}.detach();

std::unique_lock<std::mutex> lock{m};
cond.wait(lock, [&] { return val != std::thread::id{}; });

std::cout << "Thread " << val << " finished first" << std::endl;

Note: val doesn't necessarily represent the thread that finished first as all threads finish at about the same time and an overwrite might occur, but it is only for the purposes of this example.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • 4
    Note, however, that detaching a thread is a **design** decision, not a coding convenience, and is only rarely appropriate. In this case it's not needed. Once the main thread wakes up it can join the three threads. – Pete Becker Apr 25 '17 at 18:01
  • 1
    Upvoted. Once you view `.join()` as not magic, but just syntax sugar for communication among threads with mutexes and condition_variables, this is the way to go. When the default behavior of `.join()` isn't what you want, roll your own. – Howard Hinnant Apr 25 '17 at 18:28
  • 1
    Complete example: main monitors 10 threads for 1s. On average, half of them will get the job done within 1s and half won't. main prints the results out for each thread that finishes and cooperatively cancels the rest after 1s. Then main waits for all canceled threads to return before returning itself. No atomics required. Just a finite state machine communicated inter-thread (bidirectionally) with a mutex and condition_variable. It is easy to expand the communication richness of this example as much as desired. https://wandbox.org/permlink/5RjOy09nY89zqTwv – Howard Hinnant Apr 27 '17 at 00:40
  • Howard Hinnant: As I understand it, ``join()`` *is* magic (from the library user perspective) because destructors of thread-local objects are guaranteed to have finished. This cannot in general be emulated in user code. – Arne Vogel Apr 27 '17 at 16:28
2

No, there is no wait for multiple objects equivalent in C++11's threading library.

If you want to wait on the first of a set of operations, consider having them feed a thread-safe producer-consumer queue.

Here is a post I made containing a threaded_queue<T>. Have the work product of your threads be delivered to such a queue. Have the consumer read off of the other end.

Now someone can wait on (the work product) of multiple threads at once. Or one thread. Or a GPU shader. Or work product being delivered over a RESTful web interface. You don't care.

The threads themselves should be managed by something like a thread pool or other higher level abstraction on top of std::thread, as std::thread makes a poor client-facing threading abstraction.

template<class T>
struct threaded_queue {
  using lock = std::unique_lock<std::mutex>;
  void push_back( T t ) {
    {
      lock l(m);
      data.push_back(std::move(t));
    }
    cv.notify_one();
  }
  boost::optional<T> pop_front() {
    lock l(m);
    cv.wait(l, [this]{ return abort || !data.empty(); } );
    if (abort) return {};
    auto r = std::move(data.back());
    data.pop_back();
    return r;
  }
  void terminate() {
    {
      lock l(m);
      abort = true;
      data.clear();
    }
    cv.notify_all();
  }
  ~threaded_queue()
  {
    terminate();
  }
private:
  std::mutex m;
  std::deque<T> data;
  std::condition_variable cv;
  bool abort = false;
};

I'd use std::optional instead of boost::optional in C++17. It can also be replaced with a unique_ptr, or a number of other constructs.

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

It's easy to do with a polling wait:

#include<iostream>
#include<thread>
#include<random>
#include<chrono>
#include<atomic>

void thread_task(std::atomic<bool> & boolean) {
    std::default_random_engine engine{std::random_device{}()};
    std::uniform_int_distribution<int64_t> dist{1000, 3000};
    int64_t wait_time = dist(engine);
    std::this_thread::sleep_for(std::chrono::milliseconds{wait_time});
    std::string line = "Thread slept for " + std::to_string(wait_time) + "ms.\n";
    std::cout << line;
    boolean.store(true);
}

int main() {
    std::vector<std::thread> threads;
    std::atomic<bool> boolean{false};
    for(int i = 0; i < 4; i++) {
        threads.emplace_back([&]{thread_task(boolean);});
    }
    std::string line = "We reacted after a single thread finished!\n";
    while(!boolean) std::this_thread::yield();
    std::cout << line;
    for(std::thread & thread : threads) {
        thread.join();
    }
    return 0;
}

Example output I got on Ideone.com:

Thread slept for 1194ms.
We reacted after a single thread finished!
Thread slept for 1967ms.
Thread slept for 2390ms.
Thread slept for 2984ms.

This probably isn't the best code possible, because polling loops are not necessarily best practice, but it should work as a start.

Xirema
  • 19,889
  • 4
  • 32
  • 68
  • This could be improved by using a `std::condition_variable` instead of polling with a `while` loop – rwols Apr 25 '17 at 16:35
  • @rwols If you know what that code looks like, you can submit it as an answer. I'm not terribly comfortable with my use of condition variables yet. – Xirema Apr 25 '17 at 16:39
0

There is no standard way of waiting on multiple threads.

You need to resort to operating system specific functions like WaitForMultipleObjects on Windows. A Windows only example:

HANDLE handles[] = { t1.native_handle(), t2.native_handle(),  };
auto res = WaitForMultipleObjects(2 , handles, FALSE, INFINITE);

Funnily , when std::when_any will be standardized, one can do a standard but wasteful solution:

std::vector<std::thread> waitingThreads;
std::vector<std::future<void>> futures;
for (auto& thread: threads){
    std::promise<void> promise;
    futures.emplace_back(promise.get_future());
    waitingThreads.emplace_back([&thread, promise = std::move(promise)]{
         thread.join();
         promise.set_value();
    });
}

auto oneFinished = std::when_any(futures.begin(), futures.end());

very wastefull, still not available , but standard.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • do you mean `thread` instead of `t` in the `waitingThreads.emplace` lambda? (or `t` instead of `thread` in the loop? – Caleth Apr 25 '17 at 15:48