matt
matt

Reputation: 4244

What is the best way to wait a thread?

Sometimes you get the scenario where a thread needs to wait until it gets a signal from another thread. currently I achieve this in the following ways: (there is a volatile int signal; that is set by the other thread to tell the waiting thread to continue)

Method 1: Instinctively the lowest latency, but highest cpu usage while waiting:

while(signal != 1);

Method 2: Still uses 100% cpu/core but I think is better at giving other threads a chance to run? still very low latency

while(signal != 1) Sleep(0);

Method 3: Leads to negligible cpu usage while waiting (reports as 0% in task manager), but obviously has a 1ms latency.

while(signal != 1) Sleep(1);

Are there better ways to handle this case? Primarily interested in native c++ Win32, but also interested in linux/android/ios.

If there isn't a magical low latency/low waiting cpu usage solution, I am interested in the lowest latency solution in both the following cases: wait cpu usage doesn't matter, and wait cpu usage should be negligible.

Apologies if this is a bit of a noob question, but I am new to this kind of thing.

Upvotes: 2

Views: 19058

Answers (1)

Jerry Coffin
Jerry Coffin

Reputation: 490663

A lot of the "right" answer here depends on how much you value latency vs. throughput--i.e., is it more important that this specific thread starting processing as soon as possible after it's signaled, or that other threads get the maximum amount of CPU time while it's waiting? In a lot of cases, what's desired also depends on how long you expect the thread to wait before it's ready to proceed.

Right now, you're doing a busy wait--that is, while you wait, you're keeping (at least one core of) a CPU busy to some degree, checking to see if it can proceed yet.

Doing the Sleep(0) basically puts the current thread at the back of the queue of threads that are ready to execute and waiting for the CPU to execute them. So, if no other thread is waiting, it'll still consume 100% of the CPU. On the other hand, if other threads of the same priority are ready to run, they'll get a chance to run before this thread is scheduled again.

One point though: volatile isn't really defined for inter-thread communication like this, so you want to use an atomic variable instead.

std::atomic<bool> signal { false };

while (!signal)
    Sleep(0); // or Sleep(1)

This can be all right if you only expect it to take a couple of milliseconds (or something on that order) before signal becomes true so the thread can proceed. Since it's not waiting very long, it doesn't use up a lot of CPU time while it waits, and (especially if the system is lightly loaded) can respond very quickly when signal becomes true.

If the thread is likely to wait longer than a few milliseconds, you're probably better off with some mechanism that takes the current thread and marks it as not ready to run, but instead is waiting on some kernel object before it can/will be scheduled again. Windows provides Events for this purpose:

HANDLE signal = CreateEvent(/* ... */);

WaitForSingleObject(signal, INFINITE);

When the other code wants to signal that this thread should run, it does something like:

SetEvent(signal);

For this simple case, Windows events might work well--but in complex situations, getting what you want can run the ragged edge between difficult and downright impossible (though most cases where it gets difficult are really because you should probably be using something other than an event).

You can also use a condition variable from the standard library, but it's a bit more complex. A condition variable is always used in conjunction with a mutex as well as the condition you care about. So using it for your case would look a little like this:

std::atomic<bool> signal;
std::mutex m;
std::condition_variable v;

std::unique_lock<std::mutex> lock(m);
v.wait(lock, [&] { return signal; });

...and for the other code to signal that this thread can go it would do something like:

std::unique_lock<std::mutex> lock(m);
signal = true;
v.notify_one();

A condition variable does have advantages, but it's not the simplest to use, by any means. On the other hand, once you're accustomed to the boilerplate involved, it's not particularly difficult either.

Upvotes: 7

Related Questions