SSteven
SSteven

Reputation: 753

How to return a result from a thread by reference?

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?

Upvotes: 0

Views: 1056

Answers (2)

Le Ngoc Thuong
Le Ngoc Thuong

Reputation: 309

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

Upvotes: -1

Christian Hackl
Christian Hackl

Reputation: 27528

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.

Upvotes: 4

Related Questions