【问题标题】:How to fix : Executing lambda expression using pthread in c++如何修复:在 c++ 中使用 pthread 执行 lambda 表达式
【发布时间】:2019-01-07 06:28:27
【问题描述】:

我有一个 main.cpp 文件,其中包含以下代码

paral(start, end, [&](int i){
    C1[i] = A1[i] + B[i];
}, numThreads);`

我在 otherfile.cpp 中有 paral 定义,其中我有以下代码

void paral(int start, int end, T &&lambda, int nT){
    pthread_t thread1;
    int status = pthread_create(&thread1, NULL,lambda ,1);//Should execute lambda(1)
    lambda(2);//Executed by main thread
    //Code for join and so on
}

上面写着:

无法将 lambda 转换为 (void*)(*)(void *)。

我尝试将 lambda 函数强制转换并传递给 pthread,但它没有帮助。 我也想从创建的线程中调用 lambda 函数,但我无法做到。

【问题讨论】:

  • 为什么不使用 C++11 线程支持库?
  • 作为 pthread_create 的手册页状态,您提供给它的函数 (lambda) 应该将一个 void 指针作为参数(而不是您的情况下的 int),并且该参数的值是作为 pthread_create 的第四个参数给出(你的第二个 NULL)。
  • 是的,将第四个参数编辑为 1,因为我需要计算 lambda(1)
  • @P0W 应该只使用 pthread 库来完成

标签: c++ lambda pthreads


【解决方案1】:

首先你要明白Lambda是一个对象,它实现了operator()(args...)成员函数。在您的具体情况下,它是operator()(int i)

为了执行这个 lambda,必须将两个参数传递给 operator()(int)

  • Lambda 指针 (this)
  • 整数

lambda 的地址是对象(即数据)的地址,而不是代码的地址。

线程启动函数是一个接受void* 并返回void* 的函数。函数地址是机器码的地址。

因此,要执行您的 lambda,您应该定义 void* (void*) 函数并将其地址作为 start_routine 参数传递。你作为arg参数传递的lambda的地址:

template<typename Lambda>
void paral(int start, int end, Lambda&& lambda, int nT){

    struct Args
    {
        int Start;
        int End;
        Lambda& Func;
    };

    // create captureless lambda
    auto threadStart = +[](void* voidArgs) -> void* 
    {
        auto& args = *static_cast<Args*>(voidArgs);

        for(int i = args.Start; i < args.End; ++i)
            args.Func(i);
              
        return nullptr;
    };

    // I create one thread here. You will create more.    
    auto args = Args{start, end, lambda};
    pthread_t handle;
    int rc = pthread_create(&handle, NULL, threadStart, &args);

    if(rc)
        throw std::system_error(
            std::error_code(rc, std::generic_category()), 
            "pthread_create");

    pthread_join(handle, nullptr);
}

但是在这种特定情况下,您最好使用std::thread 而不是pthread 库。在这种情况下,您的代码可能如下所示:

#include <iostream>

#include <atomic>
#include <thread>
#include <vector>

template<typename Func>
void paral(int start, 
           int end, 
           Func &&func, 
           int threads_count = std::thread::hardware_concurrency())
{
    std::atomic_int counter {start};
    std::vector<std::thread> workers;
    workers.reserve(threads_count);

    for(int i = 0; i < threads_count; ++i) {
        workers.emplace_back([end, &counter, &func] {
            for(int val = counter++; val < end; val = counter++)
                func(val);
        });
    }

    for(int i = 0; i < threads_count; ++i)
        workers[i].join();
}    

int main() {
    int C1[10];
    int A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int B[10] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 110};
    paral(0, 10, [&](int i){ C1[i] = A[i] + B[i]; });

    for(auto&& v: C1)
        std::cout << v << "\n";
    std::cout << "Done. Bye!" << std::endl;
}

不过有重要提示。您的代码可能没有您预期的那么快。它将遇到false sharing 问题,因为多个线程修改同一缓存行的内存,这将迫使 CPU 内核每次在另一个 CPU 内核更新内存时更新其 L1 缓存。

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    相关资源
    最近更新 更多