Почему я получаю повторяющиеся значения при вызове функции потока C ++? - PullRequest
3 голосов
/ 17 апреля 2019

Мне любопытно, что я здесь делаю неправильно. У меня есть следующая функция:

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 <+>

- >> Готово ...

Теперь, я почти уверен, что он имеет дело со статическим для метода, но если я удаляю статическое, я получаю это:

ошибка: недопустимое использование нестатической функции-члена oid void DtIndexer :: ThreadedIndex (станд :: __ cxx11 :: строка)» threadList.emplace_back (поток (ThreadedIndex, папка));

Кроме того, если я удаляю static и добавляю функцию в поток следующим образом:

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

Я получаю:

требуется отсюда / usr / include / c ++ / 6 / функционал: 1286: 7: ошибка: статическая утверждение не выполнено: неверное количество аргументов для указателя на член static_assert (_Varargs :: значение

Я все еще довольно плохо знаком с C ++, поэтому любые советы будут оценены.

1 Ответ

3 голосов
/ 17 апреля 2019

Заменить

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, если вы действительно этого не хотите.Вы можете начать с лямбды, захватив this и folder копией.Еще одна вещь, которую вы можете заметить, заключается в том, что вывод искажается, когда многие потоки записывают в std::cout одновременно.Вы можете защитить общие ресурсы (например, std::cout) от одновременного доступа нескольких потоков, используя std::mutex и std::lock_guard.

Вот версия вашего класса с нестатическим методом потока и защитой для 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* и увидеть ту же проблему, что и у вас.

...