C ++ Создайте контейнер заданий, в который вы также можете добавить функции, которые начнутся в потоке, а затем будут удалены по завершении. - PullRequest
0 голосов
/ 30 апреля 2020

Я хочу создать контейнер, в который я могу добавить функции sh, который будет мгновенно запущен в потоке. После завершения функции ее следует автоматически удалить из контейнера, чтобы контейнер не рос бесконечно.

Вот моя попытка:

#include <thread>
#include <future>
#include <iostream>
#include <map>

class j_thread {
    std::thread thread;
public:

    j_thread() {}

    template<typename F>
    j_thread(const F& f) : thread(f) {}

    j_thread& operator = (j_thread&& other) {
        this->thread.swap(other.thread);
        return *this;
    }

    virtual ~j_thread() {
        thread.join();
    }
};

class jobs {

    std::map<size_t, j_thread> threads;

public:

    template<typename F>
    void add_job(const F &function) {

        size_t job_id = threads.size();

        auto wrapped_function = [&function, job_id, this]() {
            function();
            threads.erase(job_id);
        };

        threads[job_id] = j_thread(wrapped_function);
    }

    void wait_for_all() {
        while(threads.size() != 0) {}
    }
};


int main() {

    jobs j;
    j.add_job([](){std::cout << "hello" << std::endl;});
    j.add_job([](){std::cout << "world" << std::endl;});
    j.wait_for_all();
}

Но при запуске ошибка:

terminate called after throwing an instance of 'std::system_error'
  what():  Invalid argument
hello
terminate called recursively
12:15:44: The program has unexpectedly finished.

1 Ответ

2 голосов
/ 30 апреля 2020

Вызов join в теле потока - неопределенное поведение.

Просмотр условий ошибок для join :

Условия ошибок resource_deadlock_would_occur, если это- > get_id () == std :: this_thread :: get_id () (обнаружен тупик)

Ваше тело:

    auto wrapped_function = [&function, job_id, this]() {
        function();
        threads.erase(job_id);
    };

, где вы звоните erase, дтор * Вызывается jthread, который вызывает join в присоединяемом потоке.

Вместо join, в dtor вы должны позвонить detach.

Чтобы избежать висячей ссылки function must быть захваченным значением.

Также вы должны добавить мьютекс, чтобы избежать гонки данных на карте, при вызове size и erase:

std::mutex m;

int size() {
    std::lock_guard<std::mutex> lock{m};
    return threads.size();
}

auto wrapped_function = [f = function, job_id, this]() {
    f();
    std::lock_guard<std::mutex> l(m);
    threads.erase(job_id);
};

void wait_for_all() {
    while(size() != 0) {}
}

Demo

...