5

I have a C++11 program which configures a number of runnable objects, puts them in a std::vector, then launches them all in separate threads. Unfortunately when I iterate over the objects in the vector, I am getting threads launched only for the last object. I've distilled the problem to its core in the following test code (compiled with clang++ -std=c++11 cpp_threadlaunch.cpp using clang 6.0 on OSX 10.9.5).

#include <iostream>
#include <thread>
#include <vector>
#include <unistd.h>

std::mutex  outputlock;

class agent {

public:
    agent(std::string name) : m_name(name) {};
    ~agent(void) {};

    void run(void) {
        while (1) {
            outputlock.lock();
            std::cout << "Agent: " << m_name << std::endl;
            outputlock.unlock();
            sleep(1);
        }
    }
    std::string     getName(void) { return m_name; }

private:

    std::string     m_name;
};

int main()
{
    std::vector<std::thread>        threads;
    std::vector<agent*>             agents;
    // std::string                  goal = "succeed";
    std::string                     goal = "fail";

    agents.push_back(new agent("A"));
    agents.push_back(new agent("B"));

    if (goal == "succeed") {

        threads.push_back(std::thread([&]() { agents.at(0)->run(); }));
        threads.push_back(std::thread([&]() { agents.at(1)->run(); }));

    }
    else {

        for (auto it = agents.begin(); it != agents.end(); it++) {

            agent* a = *it;

            std::cout << "Launching thread for " << a->getName() << std::endl;

            threads.push_back(std::thread([&]() { a->run(); }));
        }
    }

    for (auto it = threads.begin(); it != threads.end(); it++) {

        it->join();
    }

    exit(0);
}

When I run with goal = "succeed", I get the hoped-for output

Agent: A
Agent: B
Agent: A
Agent: B

When I run with goal = "fail", I get two copies of the output from only one object, instead of output from each object:

Launching thread for A
Launching thread for B
Agent: B
Agent: B
Agent: B
Agent: B

I suspect I'm missing something rudimentary here -- would much appreciate it if someone can explain what's going on and what the solution is. Thanks --

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
lcikgl
  • 759
  • 1
  • 8
  • 20

2 Answers2

12

The lambda function you're passing to std::thread in the loop captures a by reference. Since a then goes out of scope you have undefined behavior. Capture it by value (using [=] instead of [&]).

erip
  • 16,374
  • 11
  • 66
  • 121
Kevin
  • 6,993
  • 1
  • 15
  • 24
2

The problem is that you capture the value of 'a' by reference in [&]() { a->run() } You see the last value, because the loop has finished, by the time the other threads run. If you capture by value, using [=] you should get the desired effect.

Anton
  • 39
  • 3
  • Did you add something what the other answer didn't have or did you just post the same, but without formatting? – LogicStuff Dec 10 '15 at 23:07
  • There was no other answer here, when I posted mine. – Anton Dec 10 '15 at 23:11
  • It says the other answer is 10 minutes older and you could have refreshed the page while typing yours. I don't post if someone beats me to the seconds. – LogicStuff Dec 10 '15 at 23:12
  • It is just my first time answering. I was assuming it worked in a more sensible way than that. – Anton Dec 11 '15 at 09:09