C++ lambda表达式线程循环

标签 c++ multithreading c++11 thread-safety

有人可以解释一下,为什么如果我将 lambda 与以下代码一起使用,那么有时线程会尝试插入相同的值,但它不可能是正确的。 (因为for循环i变量) 或者更具体地说,为什么我在 for 循环中没有被 lambda 改变?

谢谢

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <map>
#include <algorithm>
#include <string>

//#define USELAMBDA 1;

const int threadCnt = 100;
const int insertPerThreadCnt = 100;
std::mutex g_map_mutex;

void thread_function(std::string s, std::map<int, int> &map, int threadID) {
    int baseNumber = (threadCnt * insertPerThreadCnt) * threadID;
    for (int i = 0; i < insertPerThreadCnt; i++) {
        // find key what is not exists for testing
        int number = baseNumber + i;

        g_map_mutex.lock();
        std::map<int, int>::iterator it = map.find(number);
        if (map.end() == map.find(number)) {
            map[number] = i;
        }
        else {
            std::cout << "found:" << number << "/ThID:" << threadID << " Base:" << baseNumber << " i:" << i << std::endl;
        }
        g_map_mutex.unlock();
    }
}

void do_join(std::thread& t) {
    t.join();
}

void join_all(std::vector<std::thread>& v) {
    std::for_each(v.begin(), v.end(), do_join);
}

int main() {
    std::vector<std::thread> workers; // vector container stores threads
    std::map <int, int> map;
    std::string s = "Test String";

    for (int i = 0; i < threadCnt; ++i) {
#ifdef USELAMBDA
            // LAMBDA
            workers.push_back(std::thread([&]() {
                thread_function(s, std::ref(map), i);
            }));
#else
            // NORMAL
        workers.push_back(std::thread(thread_function, s, std::ref(map), i));
#endif
    }

    std::cout << "main thread\n";

#ifdef USELAMBDA
    // Looping every thread via for_each
    // The 3rd argument assigns a task
    // It tells the compiler we're using lambda ([])
    // The lambda function takes its argument as a reference to a thread, t
    // Then, joins one by one, and this works like barrier
    std::for_each(workers.begin(), workers.end(), [](std::thread &t) 
    {
        t.join();
    });
#else
    join_all(workers);
#endif

    int correctSize = threadCnt * insertPerThreadCnt;
    if (map.size() != correctSize) {
        std::cout << "Wrong size of map:" << map.size() << ", should be: " << correctSize;
    }
    else {
        std::cout << "Ready.";
    }

    return 0;
}

最佳答案

您的代码中有未定义的行为。您正在通过引用捕获 i 并且 i 在循环结束后立即停止存在,并且您无法保证到那时所有线程都会完成。无论如何,UB 除了 i 将被传递给 thread_function 函数,其值不是 lambda 创建时间,而是 i 得到的值实际调用函数时。所以,即使你很幸运并且 i 没有超出范围,它也很可能有不同的值。

将您的代码更改为:

workers.push_back(std::thread([&map, &s, i]() {
                thread_function(s, std::ref(map), i);
            }));

关于C++ lambda表达式线程循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37206321/

相关文章:

c++ - C++ 中的 cerr 和 cout 意外输出

c++ - 在文件输入过程中跟踪最高的 5 个数字

c++ - C++ 中的 QNX 面向对象线程

c++ - boost序列化库中的<<和&有什么区别?

python - 从不同线程修改 Python 字典

c# - 任务继续阻塞 UI 线程

c++ - 在新的 int[][] 上删除或删除[]

c++ - 如何访问 vector 的元素,其中 vector 在 unordered_map 中映射为字符 'key'?

c++ - 在 C++ 中使用模板参数进行矩阵乘法

c++ - Cuda 分配和返回数组从 gpu 到 cpu