0

It is possible to pass an argument to a function running in another thread, by reference.

Is it possible to return a result from a function running in another thread, by reference. If so, how?

SSteven
  • 733
  • 5
  • 17
  • 1
    How would you return any value from a function? How does it matter in which thread it is running? That said, references always require asking oneself about the lifetime of the object being referred to. – Ulrich Eckhardt Mar 10 '18 at 07:48
  • 2
    As others have stated you can't return a value from a thread, [but `std::future` is likely the next best thing.](http://en.cppreference.com/w/cpp/thread/future) – user4581301 Mar 10 '18 at 07:56

2 Answers2

4

It is possible to pass an argument to a function running in another thread, by reference.

Not directly, because all arguments are copied or moved into the other thread, but you can simulate reference passing with std::ref or std::cref. See std::thread constructor documentation:

The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).

And of course, you have to make sure that the referenced object isn't destructed before the other thread is done using it.

Here's a small example:

#include <iostream>
#include <thread>

void f(int& x)
{
    std::cout << x << '\n';
}

int main()
{
    int a = 1;
    std::thread t(f, std::ref(a));
    t.join();
    // safe, because `a` still exists at this point,
    // but the thread is already finished
}

Is it possible to return a result from a function running in another thread, by reference.

No.

First of all, that wouldn't make sense, because it would defeat the purpose of threads if the caller was blocked waiting for the called function to return (however, see below).

Second, that's just not how threads work. As the C++ standard says at §4.7/1 [intro.multithread]:

When one thread creates another, the initial call to the top-level function of the new thread is executed by the new thread, not by the creating thread.

In other words, every thread has "its own stack". This is completely different from using functions in the same thread. You cannot use return to return anything from a new thread to the original thread.

You indirectly "return" something by the other thread setting data accessible to the original thread. You can do that via a simulated reference if you want to:

#include <iostream>
#include <thread>

void f(int& x, int& result)
{
    result = x * x;
}

int main()
{
    int a = 2;
    int result;
    std::thread t(f, std::ref(a), std::ref(result));
    t.join();
    std::cout << result << '\n';
}

Outside of such toy examples, the shared data will more realistically be a std::atomic or be guarded by std::mutex / std::scoped_lock et al.


That being said, you should definitely have a look at std::future. Futures don't change the way threads work internally, but they provide an abstraction layer which resembles a normal function-call stack.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • Come to think of it, even a function called in the _same_ thread can't return a value by reference, because if a variable local to the called function were returned by reference, it would be invalid in the caller, since the variable in the called function would no loner exist (unless it were static). future and async() are good ways to return a value from a function running in another thread. – SSteven Mar 10 '18 at 15:17
  • @SSteven: *"even a function called in the same thread can't return a value by reference, because if a variable local to the called function were returned by reference, it would be invalid in the caller"* - Well, you *can* return by reference if you do not refer to a local object in the returning function, of course (e.g. a reference to a global object or to an object allocated on the free-store). But such cases should be very rare. – Christian Hackl Mar 10 '18 at 16:29
-1

Yes, but be very be careful cause each thread may using the same resource and leading to "data racing". You should use "mutex" or another mechanism to synchronize Ex: Without mutex

#include<iostream>
#include<string>
#include<vector>
#include<thread>

static std::vector<std::string> glm;
void addMap(std::vector<std::string> &glm, std::string name)
{
    glm.push_back(name);
    std::cout << "Add: " << name << "\n";
}

int main()
{
    std::thread t[4];
    std::vector < std::string> names = { "Hellen", "Peter", "Bob", "Zoe" };

    for(int i = 0; i < names.size(); i++)
    {
        t[i] = std::thread(addMap, std::ref(glm), std::ref(names[i]));
    }

    for(int i = 0; i < names.size(); i++)
    {
        t[i].join();
    }
}

As the example above, we expect it will print: Add: Hellen Add: Peter Add: Bob Add: Zoe

But it's NOT. Sometimes, it will print not enough names, sometimes the order is different. Example with mutex:

#include<iostream>
#include<string>
#include<vector>
#include<thread>
#include<mutex>
static std::vector<std::string> glm;
std::mutex mut;
void addMap(std::vector<std::string> &glm, std::string name)
{
    mut.lock();
    glm.push_back(name);
    std::cout << "Add: " << name << "\n";
    mut.unlock();
}


int main()
{
    std::thread t[4];
    std::vector < std::string> names = { "Hellen", "Peter", "Bob", "Zoe" };

    for(int i = 0; i < names.size(); i++)
    {
        t[i] = std::thread(addMap, std::ref(glm), std::ref(names[i]));
    }

    for(int i = 0; i < names.size(); i++)
    {
        t[i].join();
    }
}

Be very careful when coding with multithread

Le Ngoc Thuong
  • 279
  • 2
  • 8
  • Your examples do not compile. – Christian Hackl Mar 10 '18 at 13:37
  • What is your compiler? What is the error? Your compiler must support C++11 cause thread and mutex was release since C++11 – Le Ngoc Thuong Mar 10 '18 at 15:43
  • All conforming compilers must reject the `std::thread(addMap, glm, names[i])` line (and do so), because it's invalid C++. You cannot pass references to thread functions like that (see my answer). I suspect you are using Visual C++ without the appropriate correctness flags, e.g. Visual C++ 2017 without `/permissive-`. – Christian Hackl Mar 10 '18 at 16:26
  • P.S.: The error messages by all three compilers I've tested with are too long to copy here, but it comes down to something like `/opt/wandbox/clang-head/include/c++/v1/thread:342:5: error: attempt to use a deleted function __invoke(_VSTD::move(_VSTD::get<1>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);` – Christian Hackl Mar 10 '18 at 16:34
  • Why std::thread(addMap, glm, names[i]) is invalid? I use Visual Studio 2013, it compiles and runs fine. – Le Ngoc Thuong Mar 11 '18 at 03:48
  • Because you are trying to pass a reference directly. See https://stackoverflow.com/questions/34078208/passing-object-by-reference-to-stdthread-in-c11/34078246. I have no Visual C++ 2013 available, but I guess it *was* already able to print a diagnostic message for this case, at least with `/W4`. Why don't you try your code with a newer compiler or with stricter compiler settings? – Christian Hackl Mar 11 '18 at 10:36
  • Thank for your explanation, my mistake. With a little fix, my example is: "be careful with share resource when using multithread". :D. – Le Ngoc Thuong Mar 12 '18 at 02:10