Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a function and arguments to a thread

Problem Statement - tl;dr
Add numbers to a vector, then output them

Details

My intention was to make a class Foo, that contained a std::vector<int> that I could populate in a thread-safe manner. I created an AddValue method to allow adding values to that vector keeping thread-safety in mind.

std::mutex mt;
class Foo
{
public:
    void AddValue(int i)
    {
        std::lock_guard<std::mutex> lg{ mt };
        values.push_back(i);
    }

    void PrintValues() const
    {
        for (int i : values)
        {
            std::cout << i << " ";
        }
    }

private:
    std::vector<int> values;
};

I then made a free function that I could use to create a thread, without any knowledge of the internals of Foo.

void Func(Foo& foo, int t)
{    
    for (int i = 0; i < t; i++)
    {
        foo.AddValue(i);
    }
}

My intention was to add the following values to the vector (in whatever order they ended up due to the thread timing)

{3, 2, 2, 1, 1, 1, 0, 0, 0, 0}

Here is a quick test to see if that was working.

int main()
{
    Foo foo;
    std::vector<std::thread> threads;
    for (int i = 1; i < 5; ++i)
    {
        threads.emplace_back(Func, foo, i);
    }

    std::for_each(begin(threads), end(threads), [](std::thread& t){ t.join(); });

    foo.PrintValues();
}

Problem
The above code won't compile

This is the error message

In file included from /usr/include/c++/4.9/mutex:42:0,
                 from 3:
/usr/include/c++/4.9/functional: In instantiation of 'struct std::_Bind_simple<void (*(Foo, int))(Foo&, int)>':
/usr/include/c++/4.9/thread:140:47:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(Foo&, int); _Args = {Foo&, int&}]'
/usr/include/c++/4.9/ext/new_allocator.h:120:4:   required from 'void __gnu_cxx::new_allocator< <template-parameter-1-1> >::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = {void (&)(Foo&, int), Foo&, int&}; _Tp = std::thread]'
/usr/include/c++/4.9/bits/alloc_traits.h:253:4:   required from 'static std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {void (&)(Foo&, int), Foo&, int&}; _Alloc = std::allocator<std::thread>; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]'
/usr/include/c++/4.9/bits/alloc_traits.h:399:57:   required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {void (&)(Foo&, int), Foo&, int&}; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/include/c++/4.9/bits/vector.tcc:97:40:   required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {void (&)(Foo&, int), Foo&, int&}; _Tp = std::thread; _Alloc = std::allocator<std::thread>]'
44:42:   required from here
/usr/include/c++/4.9/functional:1665:61: error: no type named 'type' in 'class std::result_of<void (*(Foo, int))(Foo&, int)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/4.9/functional:1695:9: error: no type named 'type' in 'class std::result_of<void (*(Foo, int))(Foo&, int)>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

I don't quite understand that error message. Am I not adding the threads to the vector threads correctly?

like image 424
Cory Kramer Avatar asked Mar 15 '23 14:03

Cory Kramer


1 Answers

You have to wrap the foo in a std::ref. Threads don't allow things to be passed by reference without that wrapper.

like image 110
Carcigenicate Avatar answered Mar 18 '23 20:03

Carcigenicate