V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Huelse
V2EX  ›  C++

c++ thread 并发问题

  •  
  •   Huelse · 2020-07-15 20:39:25 +08:00 · 2676 次点击
    这是一个创建于 1595 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我想用 thread 并发处理 for 循环,但总是 join 放在循环外就会出错,是 lambda 的问题吗?

    vector<thread> threads;
    
    for (int k = 1; k < d; k++)
    {
        thread t1([&](){
            A_result[k] = Linear_Transform_Plain(A_result[0], params);
        });
        thread t2([&](){
            B_result[k] = Linear_Transform_Plain(B_result[0], params);
        });
        threads.push_back(t1);
        threads.push_back(t2);
        // join 在里面就不会出错
        // t1.join();
        // t2.join();
    }
    for(auto i = threads.begin(); i != threads.end(); i++){
        i->join();
    }
    

    报错:

    terminate called after throwing an instance of 'std::bad_alloc'
      what():  std::bad_alloc
    Aborted (core dumped)
    

    尝试过换用thread threads[14]然后索引赋值的方法,报错:

    terminate called recursively
    Aborted (core dumped)
    

    然后不用 lambda 就可以运行,但获取不到返回值了

    thread threads[14];
    
    for (int k = 1; k < d; k++)
    {
        threads[k-1] = thread(Linear_Transform_Plain, A_result[0], params);
        threads[k-1+7] = thread(Linear_Transform_Plain, B_result[0], params);
    }
    for (int k = 1; k < d; k++)
    {
        threads[k-1].join();
        threads[k-1+7].join();
    }
    

    网上找了大多是基础用法和线程池,并不能达到我想要的全核心并发处理。

    这里先感谢各位了!

    18 条回复    2020-08-13 20:42:52 +08:00
    hankai17
        1
    hankai17  
       2020-07-15 20:47:45 +08:00
    thread 不支持 copy?
    Huelse
        2
    Huelse  
    OP
       2020-07-15 21:03:56 +08:00
    @hankai17 #1 是什么意思?
    gantleman
        3
    gantleman  
       2020-07-15 21:07:49 +08:00
    从 C++的语法来说你在 for 循环里声明了两个 thread t1 t2 的局部变量。
    离开 for 循环后这两个局部变量被销毁,任何使用的操作都是非法的。
    建议你改用 new 尝试下。
    nightwitch
        4
    nightwitch  
       2020-07-15 21:08:11 +08:00   ❤️ 1
    第一个里面,不要用引用去捕获 k 的值,k 的值一直在变,而且当 for 循环结束以后,k 的生命周期结束,你的 lambda 里面的 k 就是空悬引用。
    nightwitch
        5
    nightwitch  
       2020-07-15 21:11:22 +08:00
    @gantleman t1,t2 被 push 到 vector 里面去了啊
    nannanziyu
        6
    nannanziyu  
       2020-07-15 21:17:32 +08:00   ❤️ 1
    std::thread 没有拷贝构造函数

    http://www.cplusplus.com/reference/thread/thread/thread/
    3) copy constructor
    Deleted constructor form (thread objects cannot be copied).

    所以修改 threads.push_back(t1); 为 threads.push_back(std::move(t1))
    V2WT
        7
    V2WT  
       2020-07-15 21:20:44 +08:00
    ```
    #include <iostream>
    #include <thread>
    #include <vector>
    #include <algorithm>
    int main()
    {
    // vector container stores threads
    std::vector<std::thread> workers;
    for (int i = 0; i < 5; i++) {
    workers.push_back(std::thread([]()
    {
    std::cout << "thread function\n";
    }));
    }
    std::cout << "main thread\n";

    // 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();
    });

    return 0;
    }

    ```
    I found these code, from here:
    [click]( https://www.bogotobogo.com/cplusplus/C11/3_C11_Threading_Lambda_Functions.php).
    Hope this would help!
    nannanziyu
        8
    nannanziyu  
       2020-07-15 21:23:14 +08:00   ❤️ 1
    然后还有 4 楼说的,你的 k 不能传引用,要传值
    改成
    std::thread t1([&,k]() {
    });
    Huelse
        9
    Huelse  
    OP
       2020-07-15 21:23:38 +08:00
    @nannanziyu #6
    @nightwitch #4
    感谢感谢~
    综合两位的答案,将第一个例子改成
    ```
    std::thread t1([&, k](){
    A_result[k] = ...
    });

    threads.push_back(std::move(t1));
    ```
    就可以了,核心跑满,舒服了~
    gantleman
        10
    gantleman  
       2020-07-15 21:27:12 +08:00
    @nightwitch 是的,6 楼是正确答案
    GeruzoniAnsasu
        11
    GeruzoniAnsasu  
       2020-07-15 21:29:04 +08:00   ❤️ 2
    c++11 以后不要记得容器有 push_back 这个函数

    一律用 emplace_back,这个函数会自动转发左右值引用

    emplace_back 还有一个重载是用入参构造元素,所以可以

    threads.emplace_back([&](){WHATEVER})
    billyzs
        12
    billyzs  
       2020-07-16 01:34:49 +08:00
    @GeruzoniAnsasu 这个例子直接上 emplace_back()没问题,不过明确需要拷贝左值的时候 push_back()可读性更高
    [https://abseil.io/tips/112]( https://abseil.io/tips/112)
    tusj
        13
    tusj  
       2020-07-16 09:50:16 +08:00
    threads.push_back(std::move(t1));
    Sentan
        14
    Sentan  
       2020-07-16 09:54:34 +08:00
    @nannanziyu 大佬,问下,用 std:move 处理 tread 那个对象,把他变成左值是为了什么,这样是会把这个对象的生命周期变长吗,还是说让他从栈空间变成堆空间了?
    Wirbelwind
        15
    Wirbelwind  
       2020-07-16 10:37:18 +08:00
    @Sentan thread 只有移动语义,所以不能 copy,只能转让所有权,用 std::move 告诉编译器调用移动构造
    Wirbelwind
        16
    Wirbelwind  
       2020-07-16 10:37:48 +08:00   ❤️ 1
    Sentan
        17
    Sentan  
       2020-07-16 15:03:31 +08:00
    @Wirbelwind 哦哦,明白了
    hardwork
        18
    hardwork  
       2020-08-13 20:42:52 +08:00
    你这个和 std::async + future 用法有点像的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3383 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:55 · PVG 19:55 · LAX 03:55 · JFK 06:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.