Topics: Terminating an infinite loop in C ++

I am trying to write a thread that will work against the background of my main program and the sth monitor. At some point, the main program should signal that the thread should exit safely. Here is a minimal example that writes local time to the command line at fixed intervals.

#include <cmath> #include <iostream> #include <thread> #include <future> #include <chrono> int func(bool & on) { while(on) { auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::cout << ctime(&t) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { bool on = true; std::future<int> fi = std::async(std::launch::async, func, on); std::this_thread::sleep_for(std::chrono::seconds(5)); on = false; return 0; } 

When the "on" variable is not passed by reference, this code compiles and produces the expected result, except that the stream never ends. As soon as the variable is passed by reference, I get a compiler error

 In file included from /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/thread:39:0, from background_thread.cpp:3: /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional: In instantiation of 'struct std::_Bind_simple<int (*(bool))(bool&)>': /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/future:1709:67: required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = int (&)(bool&); _Args = {bool&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]' background_thread.cpp:20:64: required from here /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional:1505:61: error: no type named 'type' in 'class std::result_of<int (*(bool))(bool&)>' typedef typename result_of<_Callable(_Args...)>::type result_type; ^ /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional:1526:9: error: no type named 'type' in 'class std::result_of<int (*(bool))(bool&)>' _M_invoke(_Index_tuple<_Indices...>) 

Could you suggest a way to fix this code?

Bonus question: What is wrong and why it works with std :: ref, but not with normal &

+5
source share
4 answers

std::ref is the start, but this is not enough. C ++ is guaranteed to know the changes in a variable by another thread if that variable is protected either by or

a) atomic, or

b) fence (mutex, condition_variable, etc.)

It is also advisable to synchronize threads before allowing main to complete. Note that I have a fi.get() call that blocks the main thread until the future is satisfied with the asynchronous thread.

updated code:

 #include <cmath> #include <iostream> #include <thread> #include <future> #include <chrono> #include <functional> #include <atomic> // provide a means of emitting to stdout without a race condition std::mutex emit_mutex; template<class...Ts> void emit(Ts&&...ts) { auto lock = std::unique_lock<std::mutex>(emit_mutex); using expand = int[]; void(expand{ 0, ((std::cout << ts), 0)... }); } // cross-thread communications are UB unless either: // a. they are through an atomic // b. there is a memory fence operation in both threads // (eg condition_variable) int func(std::atomic<bool>& on) { while(on) { auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); emit(ctime(&t), "\n"); std::this_thread::sleep_for(std::chrono::seconds(1)); } return 6; } int main() { std::atomic<bool> on { true }; std::future<int> fi = std::async(std::launch::async, func, std::ref(on)); std::this_thread::sleep_for(std::chrono::seconds(5)); on = false; emit("function returned ", fi.get(), "\n"); return 0; } 

Output Example:

 Wed Jun 22 09:50:58 2016 Wed Jun 22 09:50:59 2016 Wed Jun 22 09:51:00 2016 Wed Jun 22 09:51:01 2016 Wed Jun 22 09:51:02 2016 function returned 6 

on request, explanation emit<>(...)

 template<class...Ts> void emit(Ts&&...ts) 

emit is a function that returns void and accepts any number of parameters using a reference to the x-value (for example, the constant ref, ref or r-value of ref). That is, he will accept anything. This means that we can call:

  • emit(foo()) - call with the return value of the function (r-value)

  • emit(x, y, foo(), bar(), "text") - a call with two links, 2 r-values-links and a string literal

using expand = int[]; defines a type as an array of integers of indefinite length. We will use this simply to force the evaluation of expressions when we create an object of type expand . The actual array itself will be discarded by the optimizer - we just need the side effects of building it.

void(expand{ ... }); - forces the compiler to create an instance of the array, but the void cast says that we will never use the array itself.

((std::cout << ts), 0)... - for each parameter (denoted by ts), one member is expanded in the array construction. Remember that an array is an integer. cout << ts will return ostream& , so we use the comma operator to sequentially order the call to ostream<< before just evaluating the expression 0. Zero can actually be any integer. That would not matter. This is an integer that is conceptually stored in an array (which will still be discarded).

0, - The first element of the array is zero. This is the case when someone calls emit() with no arguments. Ts parameter package will be empty. If we did not have this leading zero, the final evaluation of the array would be int [] { } , which is a zero-length array that is illegal in C ++.

Further notes for beginners:

everything inside the array initialization list is an expression.

An expression is a sequence of "arithmetic" operations that leads to some object. This object can be an actual object (an instance of a class), a pointer, a reference, or a fundamental type, such as an integer.

So, in this context, std::cout << x is an expression evaluated by calling std::ostream::operator<<(std::cout, x) (or its equivalent function, depending on what x is) . The return value from this expression is always std::ostream& .

Putting an expression in parentheses does not change its value. It just orders. for example, a << b + c means "shifted left by (b plus c)", while (a << b) + c means "shifted left by b, then add c".

The comma ',' is also an operator. a(), b() means "call function a" and then discards the result and then calls function b. The return value must be the value returned by b '.

So, with some mental gymnastics, you should be able to see that ((std::cout << x), 0) means "calling std::ostream::operator<<(std::cout, x) , throw away the resulting link to stream, and then evaluate the value to 0.) The result of this expression is 0, but the side effect of streaming x to cout will happen before we get 0 '.

So, when Ts is (say) int and a pointer to a string, Ts... will be a similar list of types <int, const char*> and Ts... will be effectively <int(x), const char*("Hello world")>

Thus, the expression will expand to:

 void(int[] { 0, ((std::cout << x), 0), ((std::cout << "Hello world"), 0), }); 

What steps in a child means:

  • select an array of length 3
  • array [0] = 0
  • call std :: cout <x, discard the result, array [1] = 0
  • call std :: cout <"Hello world", drop the result, array [2] = 0

And, of course, the optimizer sees that this array is never used (because we did not give it a name), so it removes all unnecessary bits (because optimizers do it), and it becomes equivalent:

  • call std :: cout <x, discard the result.
  • call std :: cout <"Hello world", throw away the result.
+5
source

I see two questions with your approach:

  • As The Dark mentions, you are referring to a boolean value that could go out of scope because your main exists after setting on = false
  • you should try to make the compiler know that the on value may change during the execution of the loop. If you do not, some compiler optimizers may simply replace an infinite loop.

A simple solution would be to place the flag in global memory:

 static std::atomic<bool> on = false; void func() { while (on) /* ... */ } int main() { on = true; /* launch thread */ /* wait */ on = false; return 0; } 
+4
source

Bonus Question: what is wrong and why does it work with std :: ref, but not with normal &

Because std::async , as well as similar interfaces like std::thread and std::bind , take arguments by value. When you pass the reference byte value, then the referenced object is copied as an argument. When you use std::reference_wrapper , the wrapper is copied and the link inside remains intact.

In case you are wondering why it is designed to work as follows, check out this answer on SO

+2
source

As the good guy Simon suggested, one way to do this is to use std :: ref

 #include <cmath> #include <iostream> #include <thread> #include <future> #include <chrono> #include <functional> int func(std::reference_wrapper<bool> on) { while(on) { auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::cout << ctime(&t) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { bool on = true; std::future<int> fi = std::async(std::launch::async, func, std::ref(on)); std::this_thread::sleep_for(std::chrono::seconds(5)); on = false; return 0; } 
-1
source

All Articles