2013-10-07 36 views
2

我有一个模式匹配程序,它需要一个字符串作为输入,并返回一个字典紧密匹配的字符串。由于该算法需要几秒钟来运行一个匹配查询,因此我试图使用多线程来运行批量查询。C++从几个线程返回结果到一个数组

我首先读入一个包含查询列表的文件,并为每个查询调度一个新线程来执行匹配算法,并使用pthread_join将结果返回给一个数组。

但是,我收到了一些不一致的结果。例如,如果我的查询文件包含术语“红色,绿色,蓝色”,我可能会收到“红色,绿色,绿色”的结果。另一次运行可能会产生正确的“红色,绿色,蓝色”结果。它似乎有时会写入数组中的结果,但为什么会发生这种情况,因为数组值是根据线程ID设置的?

Dictionary dict; // global, which performs the matching algorithm 

void *match_worker(void *arg) { 
    char* temp = (char *)arg; 
    string strTemp(temp); 
    string result = dict.match(strTemp); 
    return (void *)(result.c_str()); 
} 

void run(const string& queryFilename) { 
    // read in query file 
    vector<string> queries; 
    ifstream inquery(queryFilename.c_str()); 
    string line; 
    while (getline(inquery, line)) { 
     queries.push_back(line); 
    } 
    inquery.close(); 

    pthread_t threads[queries.size()]; 
    void *results[queries.size()]; 
    int rc; 
    size_t i; 

    for (i = 0; i < queries.size(); i++) { 
     rc = pthread_create(&threads[i], NULL, match_worker, (void *)(queries[i].c_str())); 
     if (rc) { 
      cout << "Failed pthread_create" << endl; 
      exit(1); 
     } 
    } 

    for (i = 0; i < queries.size(); i++) { 
     rc = pthread_join(threads[i], &results[i]); 
     if (rc) { 
      cout << "Failed pthread_join" << endl; 
      exit(1); 
     } 
    } 

    for (i = 0; i < queries.size(); i++) { 
     cout << (char *)results[i] << endl; 
    } 
} 

int main(int argc, char* argv[]) { 
    string queryFilename = arg[1]; 
    dict.init(); 
    run(queryFilename); 
    return 0; 
} 

编辑:正如扎克建议,我修改了线程明确把结果在堆上:

void *match_worker(void *arg) { 
    char* temp = (char *)arg; 
    string strTemp(temp); 
    int numResults = 1; 
    cout << "perform match for " << strTemp << endl; 
    string result = dict.match(strTemp, numResults); 
    string* tmpResult = new string(result); 
    return (void *)((*tmpResult).c_str()); 
} 

虽然,在这种情况下,如果我把删除电话?如果我尝试在run()函数的末尾放置以下内容,它会给出无效的指针错误。

for (i = 0; i < queries.size(); i++) { 
    delete (char*)results[i]; 
} 
+0

什么类型是Dictionary?线程安全吗?它听起来好像不是。 – stonemetal

回答

4

没有调试它,我的猜测是,它是与以下几点:

void *match_worker(void *arg) 
{ 
    char* temp = (char *)arg; 
    string strTemp(temp); 
    string result = dict.match(strTemp); // create an automatic 
    return (void *)(result.c_str()); // return the automatic ... but it gets destructed right after this! 
} 

所以,当一个线程运行时,它写了你都指向同一个内存位置(偶然),并且你插入相同的值两次(不写在它上面)。

您应该将结果放在堆上,以确保它不会在线程退出并将其存储在主线程中时被破坏。

随着你的编辑,你正尝试混合一些东西太多。我已经固定在它下面:

void *match_worker(void *arg) 
{ 
    char* temp = (char *)arg; 
    string strTemp(temp); 
    int numResults = 1; 
    cout << "perform match for " << strTemp << endl; 
    string result = dict.match(strTemp, numResults); 
    string* tmpResult = new string(result); 
    return (void *)(tmpResult); // just return the pointer to the std::string object 
} 

声明results作为

// this shouldn't compile 
//void* results[queries.size()]; 
std::string** results = new std::string[queries.size()]; 
for (int i = 0; i < queries.size(); ++i) 
{ 
    results[i] = NULL; // initialize pointers in the array 
} 

当你清理内存:

for (i = 0; i < queries.size(); i++) 
{ 
    delete results[i]; 
} 
delete [] results; // delete the results array 

这就是说,你将不得不如果你可以更容易地使用C++11 threading templates而不是混合C pthread库和C++。

+0

谢谢!请参阅我的编辑。 – Aaron

+1

@Aaron编辑解决您的新问题。 –

2

该问题是由局部变量result的生存期和成员函数result.c_str()返回的数据造成的。通过将C与C++混合,使得这项任务变得不必要的困难。考虑使用C++ 11及其线程库。它使得任务更容易:

std::string match_worker(const std::string& query); 

void run(const std::vector<std::string>& queries) 
{ 
    std::vector<std::future<std::string>> results; 
    results.reserve(queries.size()); 
    for (auto& query : queries) 
     results.emplace_back(
      std::async(std::launch::async, match_worker, query)); 
    for (auto& result : results) 
     std::cout << result.get() << '\n'; 
}