使用lambda捕获局部变量启动c ++ 11线程

时间:2016-03-31 05:41:54

标签: multithreading c++11 boost

我有一个相当基本的问题,并且不确定它的来源:并发环境中的lambda捕获评估或者对文件系统库的误用。
这是示例代码:

#include <iostream>
#include <vector>
#include <thread>
#include <boost/filesystem.hpp>

using namespace std;
using namespace boost::filesystem;

void query(const string filename)
{
    cout << filename << " ";
}

int main() {
    path p("./");
    vector<thread> thrs;

    for(auto file = directory_iterator(p); file != directory_iterator(); ++file)
    {
        thread th( [file] {query(file->path().string());} );
        thrs.push_back(move(th));
    }

    for (auto& t : thrs)
        t.join();

    return 0;
}

在运行时给出:

:~/workspace/sandbox/Release$ l
main.o  makefile  objects.mk  sandbox*  sources.mk  subdir.mk
:~/workspace/sandbox/Release$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/a/custom-libs/boost-1_59/lib/ ./sandbox 
./subdir.mk ./sources.mk ./sandbox ./objects.mk ./main.o ./main.o

注意竞争条件 - 并非所有文件最终都传递给线程函数(在此运行时 makefile 缺失)。
我能够通过在局部var中提取参数来找到解决方法,将循环体重写为:

    auto fn = file->path().string();
    thread th( [fn] {query(fn);} );
    thrs.push_back(move(th));

比赛条件来自哪里?
在创建线程时是不是 file-&gt; path()。string()

2 个答案:

答案 0 :(得分:2)

  

在线程创建时,isn&lt; file&gt; path()。string()是否已被评估?

没有。由于必须在新线程上调用query,因此必须在新线程上执行语句query(file->path().string())。所以它在线程创建之后执行了一段时间,当线程开始做事时。

您抓获了file。周期。

它在概念上与以下内容没有区别:

string * j = new string ("hello");
thread th( [j] { cout << *j << std::endl; } );
*j = "goodbye";
th.join();

您抓获了j。不是*j。虽然j的值不会发生变化,但j所指的事物的价值会发生变化。所以谁知道线程最终取消引用它会是什么。

您可能认为自己已经捕获了迭代器的价值,因此您可以放心,因为这个价值不会发生变化。不幸的是,这并不是这个迭代器的实现方式。它的实现方式允许它在递增时不可挽回地丢弃先前的信息,因此增加这种类型的迭代器也会增加迭代器的副本。

如果你捕捉某些东西的值,这些东西指的是你没有捕获的值,那么如果第二个值被改变,你现在捕获的东西就会引用不同的值。始终确切地知道您捕获的内容以及捕获方式。可悲的是,你不能安全地捕捉你不太了解的课程的价值实例。

答案 1 :(得分:1)

线程的lambda函数中的代码在创建线程时不会被评估,但是当线程被执行时。根据OS调度创建线程后,这可能是一个任意时间。

它不起作用的真正原因是因为a directory_iterator is a single pass iterator,意味着无法复制它,推进一个副本并再次使用旧副本。通过捕获,您正是这样做的。您可以修改程序以在创建线程之前提取路径并捕获它:

int main() {
    path p("./");
    vector<thread> thrs;

    for(auto file = directory_iterator(p); file != directory_iterator(); ++file)
    {
        thrs.emplace_back([path = file->path()] {query(path.string());});
    }

    for (auto& t : thrs)
        t.join();

    return 0;
}