Passing arguments to a thread
We know how to construct a thread without any input
arguments. But a function printNTimes(int n, const std::string&
msg)
requires two arguments. How to run it in a separate
thread is described below.
std::string msg = "Today is a beautiful day.";
std::thread t(printNTimes, 5, msg);
The code runs printNTimes
with arguments 5
and msg
in a new thread. But something unexpected happens if
you don’t have a const reference. Let us consider a
referenceArguments.cpp
below
#include <iostream>
#include <thread>
#include <algorithm>
void toUpper(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
int main()
{
std::string str = "Today is a beautiful day.";
std::cout << "Before: " << str << std::endl;
std::thread t(toUpper, str);
t1.join();
std::cout << "After: " << str << std::endl;
return 0;
}
This looks perfectly reasonable, at least to me. But it doesn’t compile.
$ g++ referenceArguments.cpp -std=c++11
In file included from /usr/include/c++/4.8/thread:39:0,
from referenceArguments.cpp:2:
/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<void (*(std::basic_string<char>))(std::basic_string<char>&)>’:
/usr/include/c++/4.8/thread:137:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(std::basic_string<char>&); _Args = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >&}]’
referenceArguments.cpp:16:32: required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(std::basic_string<char>))(std::basic_string<char>&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.8/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(std::basic_string<char>))(std::basic_string<char>&)>’
_M_invoke(_Index_tuple<_Indices...>)
The error message is quite cryptic. The solution is to add
std::ref
around str
:
std::thread t(toUpper, std::ref(str));
Explanation
The reasoning behind it is that a thread, during a construction, takes a function and copies all of the arguments to its internal storage. Then, accordingly to the function signature, thread passes the copies to the function by values or references. If the function expects a reference, it gets a reference to an internal copy of the object.
In the example
std::thread t(printNTimes, 5, msg);
the thread t
copies arguments 5
and
msg
to its internal storage. Then, the function
printNTimes
recieves a reference to the internal copy of the
msg
not a reference to the original msg
.
If we use std::ref
, the function correctly gets a reference
to the original object, like in the example
std::thread t(toUpper, std::ref(str));
In the case of non const reference, we get a compiler error if we don’t use the
std::ref
, but in the case of const reference we don’t get an
error . Therefore, I recommend using std::ref
every time when
we have references.
Another example
Here is arguments.cpp
, which uses the
printNTimes
function from above.
#include <iostream>
#include <thread>
void printNTimes(int n, const std::string& msg)
{
for (int i = 0; i != n; i++)
{
std::cout << msg << std::endl;
}
}
int main()
{
std::string msg1 = "Today is a beautiful day.";
std::thread t1(printNTimes, 6, std::ref(msg1));
std::string msg2 = "And sun is shining.";
std::thread t2(printNTimes, 6, std::ref(msg2));
t1.join();
t2.join();
return 0;
}
The output might be:
$ ./arguments
Today is a beautiful day.And sun is shining.
Today is a beautiful day.And sun is shining.
And sun is shining.
And sun is shining.
And sun is shining.
And sun is shining.
Today is a beautiful day.
Today is a beautiful day.
Today is a beautiful day.
Today is a beautiful day.
It looks a bit strange. This is because of so called data races.
Summary
We learned how to pass an argument to a function, which will run in a separate
thread. The std::ref
is used for passing a reference to a
variable.
In the next article, we will investigate why is the output of the final program strange – in concurrent language: we will explore data races.
Links:
-
Source code: arguments.cpp and referenceArguments.cpp