【问题标题】:Why am I getting duplicate values in a C++ thread function call?为什么我在 C++ 线程函数调用中得到重复值?
【发布时间】:2019-04-17 17:56:31
【问题描述】:

我很好奇我在这里做错了什么。我有以下功能:

indexer.h:

class DtIndexer {
public:
    static void ThreadedIndex(string folderPath);

indexer.cpp

void DtIndexer::ThreadedIndex(string folderPath) {
    cout << "\t-> Indexing Folder: " << folderPath << endl;
    cout << "\t->> Done..." << endl;
}

以及我创建线程的调用:

void DtIndexer::UpdateIndex(DatabaseData &data, bool isCreate) {
    vector<thread> threadList;

    for (string &s: data.FilePaths) {
        const char *folder = GetFolderPath(s, data.IncludeSubFolders);
        cout << "\t-> Adding Folder to Thread: " << folder << endl;
        threadList.emplace_back(thread(ThreadedIndex, folder));
    }

    for_each(threadList.begin(), threadList.end(), mem_fn(&thread::join));
}

我的输出是这样的:

-> 将文件夹添加到线程:/index_2185

-> 将文件夹添加到线程:/index_1065

-> 索引文件夹:/index_1065

->> 完成...

-> 索引文件夹:/index_1065

->> 完成...

现在,我很确定它必须处理该方法的静态,但如果我删除静态,我会得到:

错误:无效使用非静态成员函数'void DtIndexer::ThreadedIndex(std::__cxx11::string)’ threadList.emplace_back(thread(ThreadedIndex, folder));

另外,如果我删除 static 并将函数添加到线程中,如下所示:

threadList.emplace_back(thread(&DtIndexer::ThreadedIndex, folder));

我明白了:

这里需要 /usr/include/c++/6/functional:1286:7: 错误:静态 断言失败:指向成员的参数数量错误 static_assert(_Varargs::value

我对 C++ 还是很陌生,所以,任何建议都将不胜感激。

【问题讨论】:

  • GetFolderPath 是做什么的?
  • 当非static 时,必须在DtIndexer 实例上调用DtIndexer::ThreadedIndex 以获得有效的this。该实例是您缺少的参数。如果您不需要this,最好留下static 或使用free function
  • 您是否知道在这一行中thread(ThreadedIndex, folder) 被称为thread(&amp;ThreadedIndex, const char*),其中第二个参数指的是局部变量,并且当for 循环的迭代结束时,字符串被销毁。因此,当执行线程体时,字符串folderPath 是从悬空指针创建的。您应该按值而不是 const char* 将字符串传递给线程。
  • @rafix07 是的,谢谢。这就是问题所在。我还将查看添加的信息 user4581301。再次感谢大家。如果您想将此作为答案发布(针对我的具体问题),我会将其标记为已接受
  • 您的问题缺少minimal reproducible example

标签: c++ multithreading threadpool


【解决方案1】:

替换

const char *folder = GetFolderPath(s, data.IncludeSubFolders);

std::string folder( GetFolderPath(s, data.IncludeSubFolders) );

线程首先按值复制参数,然后复制const char*,它可能指向GetFolderPath 函数中的静态缓冲区。每次调用都会覆盖该缓冲区,因此从中读取的线程可能会也可能不会得到您期望的结果。

请注意,您不需要执行emplace_back(std::thread(...,因为您作为参数写入emplace_back 的内容将转发给您的vector 所持有的类型的构造函数,在本例中为std::thread。您也不需要创建线程函数static,除非您真的想要。您可以从 lambda 开始,通过复制捕获 thisfolder。您可能会注意到的另一件事是,当多个线程同时写入std::cout 时,输出会出现乱码。您可以使用std::mutexstd::lock_guard 保护公共资源(如std::cout)免受多个线程同时访问。

这是你的类的一个版本,它具有非静态线程方法和std::cout 的保护:

#include <iostream>
#include <vector>
#include <cstring>
#include <mutex>
#include <thread>

const char* GetFolderPath() {
    static std::vector<std::string> paths = {"this is a path", "here is another one",
                                             "what do we have here", "mother of dirs",
                                             "what did the tomatoe ..."};
    static size_t idx = 0;
    static char buf[256];
    std::strcpy(buf, paths[idx].c_str());
    idx = (idx + 1) % paths.size();
    return buf;
}

class DtIndexer {
    std::mutex mtx_cout; // std::cout mutex

public:
    void ThreadedIndex(std::string folderPath) {
        std::lock_guard lock(mtx_cout); // wait until this thread acquires the mutex
        std::cout << "\t-> Indexing Folder: " << folderPath << "\n";
    }

    void UpdateIndex() {
        std::vector<std::thread> threadList;

        { // lock_guard scope
            std::lock_guard lock(mtx_cout); // acquire mutex

            for(int i = 0; i < 50; ++i) {
                // const char* folder = GetFolderPath();
                std::string folder(GetFolderPath());

                std::cout << "\t-> Adding Folder to Thread: " << folder << "\n";

                //             safe copy here --+       +--- thread runs here ---+
                //                              |       |                        |
                //                              V       V                        V
                threadList.emplace_back( [this, folder] { ThreadedIndex(folder); });
                //                      Δ
                //                      |
                //                      +-- no std::thread(... needed here
            }
        } // lock_guard releases mutex here

        for(auto& t : threadList) t.join();
    }
};

int main() {
    DtIndexer a;
    a.UpdateIndex();
}

您可以将上面的 folder 字符串替换为 const char* 并查看您遇到的相同问题。

【讨论】:

  • 谢谢,这很有意义,并且与其他 cmets 的建议一致。我感谢详细的解释。
猜你喜欢
  • 2020-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
相关资源
最近更新 更多