在C 多线程应用程序中,我应该通过参数传递lambda或函数



我刚刚开始学习C 中的多线程...t1t2

是否有区别
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mutexCout;
//prints the value of x, and then increments it
//param: int value to display and increment
void foo (int& x)
{
    std::lock_guard<std::mutex> guard_(mutexCout);
    std::cout << "x is " << x << "n";
    ++x;
}
//testing different ways to call a function with reference
int main() 
{
    int x = 5;
    //is t1 different from t2???
    std::thread t1 ([&] {foo(x)};
    std::thread t2 (foo, std::ref(x));
    {
        std::lock_guard<std::mutex> guard_(mutexCout);
        std::cout << "x is " << x << "n";
    }
    //added after posting the question
    t1.join();
    t2.join();
    return 0;
}

我假设您将其用加入的呼叫修复。

使用 [&]捕获对当前范围或在其他线程中运行的lambdas的捕获通常是一个坏主意:在这两种情况下,您都应该很少有很少的共享状态,这些状态明确地说明了您所共享的内容,例如[&x]并不是一个严重的状态高架问题,并且由于看似无害的错别字或命名错误,您将阻止您以危险的方式意外地共享错误的数据。

通常,仅在应用程序小而简单且螺纹限制时,使用应用程序中的RAW C std螺纹API才有用。过去,您需要线程池,连续性,信号等。C 线程原始图足以编写它们,但它们不提供这些。

我发现,当我写自己的包装纸时,期望呼叫者通过无效的可召唤(或者只有一个有参数的呼叫框架期望提供的螺纹框架提供)比做std所做的事情要更理智。也可以通过参数。通过在lambda语法中添加C 14,这变得更加可行,该语法允许在参数中移动并计算出绑定。

所以在"真实"代码中看起来像:

my_future<void> r = some_thread_pool.add_task([&x] {foo(x)});

在运行时行为方面,两者之间几乎没有区别,但是在使用参考和线程使用通用捕获时,我会非常谨慎。例如。上面的两个代码都将地址传递给main中的堆栈,没有任何保证在线程通过此地址间接时仍在执行main。因此不确定的行为。随着兰巴斯(Lambdas)的普遍捕获,这更有可能偶然发生。使用值捕获或显式捕获更安全。

我不确定我同意上面的评论,即lambda正在使用更复杂的语言功能。在这两种情况下,都必须包装一个结构以包含参数状态以通过线程边界。对于这种情况,由于调用仅将一个指针传递给线程函数,因此可以在没有中间结构的情况下完成。(lambda版本可能必须使用结构。)但是,在论证转发等方面仍然存在相当多的C 复杂性。

如果人们对正在发生的事情真的很感兴趣,则最好查看从一个好的编译器中生成的组件,对于这样的小示例来说,这是非常可行的。(与旧学校C中的直接Pthreads代码相比,也可能是有启发性的。)我现在没有时间这样做,但是我非常好的猜测是,与正确性,可移植性和代码可读性相比,差异是无关紧要的。

我确实发现Lambdas是一个非常混杂的包,无论是使代码更容易或难以阅读。如果一个人只是将一些参数传递给线程函数,那么似乎很难阅读lambda,因为必须验证lambda中卷曲括号内的任何幻想。(并且是C ,因此通过操作员的过载等,看起来像函数调用可能很复杂。)如果一个人将整个执行放在lambda中,并且很小,因此保存必须去看一个独立的单独线程功能,然后可能会使代码易于阅读。

最新更新