2016-03-31 48 views
0

我有一個相當基本的問題,並不確定它來自哪裏:在併發環境中的lambda捕獲評估或濫用boost文件系統庫。
這是示例代碼:用lambda捕獲本地變量啓動C++ 11線程

#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 

通知的競爭條件 - 不是所有的文件最終會被傳遞給線程函數(在此運行生成文件缺失)。
我能找到在一個局部變量中提取參數,改寫循環體作爲一種解決方法:

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

在哪裏競爭條件是從哪裏來的?
是不是file-> path()。string()在創建線程時是否正確?

+0

所有這些線程同時使用std :: cout ... – kfsone

回答

2

是不是文件 - >路徑()。字符串()在創建線程時正確計算?

不需要。由於必須在新線程上調用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

當創建線程時,線程的lambda函數中的代碼並沒有被執行,而是在線程被執行時。這可能是創建線程後的任意時間,具體取決於操作系統調度。

它不起作用的真正原因是因爲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; 
}