242 / 208 / 36
Регистрация: 19.02.2021
Сообщений: 1,431

Пул потоков - непонятки

22.10.2023, 18:13. Показов 1578. Ответов 25
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!
Для своей демки честно стырил в инете вариант реализации пула потоков.
Посмотрел его, поковырял - вроде, все понятно, но все же не все)))
Вот его реализация:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <queue>
#include <thread>
#include <chrono>
#include <mutex>
#include <future>
#include <unordered_set>
#include <atomic>
 
#include <vector>
#include <chrono>
 
// C++ 14
class thread_pool {
public:
    thread_pool(uint32_t num_threads) {
        threads.reserve(num_threads);
        for (uint32_t i = 0; i < num_threads; ++i) {
            threads.emplace_back(&thread_pool::run, this);
        }
    }
 
    template <typename Func, typename ...Args>
    int64_t add_task(const Func& task_func, Args&&... args) {
        int64_t task_idx = last_idx++;
 
        std::lock_guard<std::mutex> q_lock(q_mtx);
        q.emplace(std::async(std::launch::deferred, task_func, args...), task_idx);
        q_cv.notify_one();
        return task_idx;
    }
 
    void wait(int64_t task_id) {
        std::unique_lock<std::mutex> lock(completed_task_ids_mtx);
        completed_task_ids_cv.wait(lock, [this, task_id]()->bool {
            return completed_task_ids.find(task_id) != completed_task_ids.end();
            });
    }
 
    void wait_all() {
        std::unique_lock<std::mutex> lock(q_mtx);
        completed_task_ids_cv.wait(lock, [this]()->bool {
            std::lock_guard<std::mutex> task_lock(completed_task_ids_mtx);
            return q.empty() && last_idx == completed_task_ids.size();
            });
    }
 
    bool calculated(int64_t task_id) {
        std::lock_guard<std::mutex> lock(completed_task_ids_mtx);
        if (completed_task_ids.find(task_id) != completed_task_ids.end()) {
            return true;
        }
        return false;
    }
 
    ~thread_pool() {
        quite = true;
        for (uint32_t i = 0; i < threads.size(); ++i) {
            q_cv.notify_all();
            threads[i].join();
        }
    }
 
private:
 
    void run() {
        while (!quite) {
            std::unique_lock<std::mutex> lock(q_mtx);
            q_cv.wait(lock, [this]()->bool { return !q.empty() || quite; });
 
            if (!q.empty()) {
                auto elem = std::move(q.front());
                q.pop();
                lock.unlock();
 
                elem.first.get();
 
                std::lock_guard<std::mutex> lock(completed_task_ids_mtx);
                completed_task_ids.insert(elem.second);
 
                completed_task_ids_cv.notify_all();
            }
        }
    }
 
    std::queue<std::pair<std::future<void>, int64_t>> q; // очередь задач - хранит функцию(задачу), которую нужно исполнить и номер данной задачи
    std::mutex q_mtx;
    std::condition_variable q_cv;
 
    std::unordered_set<int64_t> completed_task_ids;      // помещаем в данный контейнер исполненные задачи
    std::condition_variable completed_task_ids_cv;
    std::mutex completed_task_ids_mtx;
 
    std::vector<std::thread> threads;
 
 
    std::atomic<bool> quite{ false };                    // флаг завершения работы thread_pool
    std::atomic<int64_t> last_idx = 0;                   // переменная хранящая id который будет выдан следующей задаче
};
Меня интересует функция
C++
1
2
3
4
5
6
7
    void wait_all() {
        std::unique_lock<std::mutex> lock(q_mtx);
        completed_task_ids_cv.wait(lock, [this]()->bool {
            std::lock_guard<std::mutex> task_lock(completed_task_ids_mtx);
            return q.empty() && last_idx == completed_task_ids.size();
            });
    }
Во-первых, моя демка в ней "зависает", во-вторых, я не совсем понимаю, как работает conditional_variable completed_task_ids_cv
Честно пытался читать в инете, но недопонимаю.
Lock, как я понимаю, это мьютекс(q_mtx), который предварительно захвачен.
completed_task_ids_cv.wait() должен этот мьютекс освободить, если лямбда вернет true. Правильно?
Как выполняется тело лямбды? Крутится постоянно, пока не выполниться условие? Или как?
У меня "висит" на строке
std::unique_lock<std::mutex> lock(q_mtx) - такое впечатление, что wait_all() вызвался повторно, до освобождения мьютекса q_mtx и не может его захватить.
Но по стеку видно, что повторного вызова нет.
Может я чего-то недопонимаю?

По алгоритму демки, я "подкидываю" задачи в пул потоков, в месте, где нужны результаты, жду при помощи вот этой вот функции wait_all()
Подавляющее количество времени оно все работает. Но иногда, очень редко, "зависает" в этой функции на этой строке.

Демку могу приаттачить, но она без wxWidgets не соберется.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
22.10.2023, 18:13
Ответы с готовыми решениями:

Есть ли в C++ пул потоков
Перерыл много ссылок и не мог найти есть ли в с++ пул потоков,все источники указывают на с# ThreadPol,у меня курсовая пул потоков а...

Пул потоков thread
Здравствуйте. Изучаю параллельное программирование. С огромным трудом мне дается пул потоков. Решил попробовать написать пул потоков...

Пул потоков с семафорами
Задача:написать свой пуль потоков Написал вот такой код #include &lt;windows.h&gt; #include &quot;Worker.h&quot; #include&lt;list&gt; ...

25
274 / 206 / 31
Регистрация: 26.11.2022
Сообщений: 891
24.10.2023, 12:25
Студворк — интернет-сервис помощи студентам
признак простоя пула - это не пустота в списке задач.
Я у себя в пуле потоков делал два атомарных счётчика - количество работающих потоков в пуле и счётчик активных потоков.
Перед вызовом функции задания из очереди счётчик увеличивался а после возврата - уменьшался.

И для завершения пула потоков вначале блокировалось добавление новых заданий в очередь, а если оно происходило - то задания выполнялись сразу на мете. Потом ожидалось опустошение очереди, потом ожидалось обнуление счётчика.
решение имеет недостатки но надёжное.
0
242 / 208 / 36
Регистрация: 19.02.2021
Сообщений: 1,431
24.10.2023, 16:05  [ТС]
Цитата Сообщение от Aledveu Посмотреть сообщение
признак простоя пула - это не пустота в списке задач.
Я не список задач имел ввиду. Понятно, что список может быть пуст, но какие-то задачи еще выполняются.
Я имел ввиду список задач, выполняющихся в текущий момент.

Цитата Сообщение от Aledveu Посмотреть сообщение
Я у себя в пуле потоков делал два атомарных счётчика - количество работающих потоков в пуле и счётчик активных потоков.
Перед вызовом функции задания из очереди счётчик увеличивался а после возврата - уменьшался.
Вот это больше подходит.
Но можно вообще одним счетчиком обойтись - количеством потоков, работающих в текущий момент + очередь ожидающих задач.
0
242 / 208 / 36
Регистрация: 19.02.2021
Сообщений: 1,431
25.10.2023, 15:37  [ТС]
Все-таки не получется разобраться. Разьясните, пожалуйста, этот момент с contitional_variable:
1. В функцию wait() передается захваченный мьютекс и некое условие.
2. В функции wait() contitional_variable освобождает этот мьютекс и ждет? Или проверяет условие? Что там внутри происходит?
В документации сказано, что из какого-то другого потока должен быть послан сигнал notify_one() или notify_all() этой же contitional_variable для того, чтобы проинформировать ее, что условие поменялось.
Правильно ли я понимаю, что contitional_variable не проверяет это условие постоянно, а ждет сигнала и только после него проверяет, и если оно выполнено - захватывает мьютекс и выходит из wait()
Если это так - тогда зачем вообще это условие, когда все равно нужен внешний сигнал?
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,828
25.10.2023, 17:14
Цитата Сообщение от Constcat Посмотреть сообщение
В функции wait() contitional_variable освобождает этот мьютекс и ждет?
Да.

Цитата Сообщение от Constcat Посмотреть сообщение
Или проверяет условие?
Проверяет после получения сигнала.

Цитата Сообщение от Constcat Посмотреть сообщение
Если это так - тогда зачем вообще это условие, когда все равно нужен внешний сигнал?
Сигнал пробуждает от ожидания, но из ожидания можно выйти и самопроизвольно по каким-либо системным причинам, поэтому условие нужно, чтобы точно определить корректность пробуждения.
1
242 / 208 / 36
Регистрация: 19.02.2021
Сообщений: 1,431
25.10.2023, 18:46  [ТС]
Цитата Сообщение от DrOffset Посмотреть сообщение
Сигнал пробуждает от ожидания, но из ожидания можно выйти и самопроизвольно по каким-либо системным причинам, поэтому условие нужно, чтобы точно определить корректность пробуждения.
Получается, что wait() ждет сигнала, даже если условие изначально выполнено?
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,828
25.10.2023, 19:37
Constcat, не, первый раз условие проверяется до ожидания. Так что если оно уже выполнено, то ожидания не будет.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.10.2023, 19:37

Квази пул потоков
Здравствуйте, Подскажите, вот попытался сделать условно &quot;макетную плату&quot; пула потоков на простых элементах, естественно использовать в...

Передача функции с параметрами в пул потоков
Здравствуйте, имеется многопоточный producer consumer. Он должен вызывать функцию ProcessRequest с параметром полученным из GetRequest. К...

Пул потоков на основе boost::lockfree::queue
В книге &quot;Энтони Уильямс - Параллельное программирование на С++ в действии&quot; обсуждается пул потоков на основе самописной потокобезопасной...

Реализовать пул потоков, в который можно помещать функцию
не могу реализовать как в книге главу 9 пул потоков который,в который можно помещать функцию что бы она возвращала значение вот код который...

Можно ли сделать доступным для всех классов пул потоков?
У меня есть пул потоков который я хочу сделать доступным для всех чтолибоделающих классов. Есть класс основной Base и два класса которые...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
26
Ответ Создать тему
Опции темы

Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru