Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.67/12: Рейтинг темы: голосов - 12, средняя оценка - 4.67
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22

Оптимизировать ThreadPool на 8 потоков и задач

02.05.2023, 21:06. Показов 3164. Ответов 55
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Мое почтение, джентльмены.
Все работает, но хочу оптимизировать код, к примеру, для 8 потоков и задач.
Слишком долго выполняется на малых объёмах вычислений, собственно поэтому и хочу реализовать ThreadPool.
Из задуманного, удалить стек/очередь std::queue и операции std::move, tasks.pop(), tasks.emplace, возможно что-то еще.
Я точно знаю. что буду запускать количество потоков и задач = std::thread::hardware_concurrency() (количеству логических ядер), поэтому очередь не нужна (будет фикс блок все время). Прошу поделится вашей реализацией или годным советом.


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
100
101
102
103
104
105
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
#include <type_traits>
 
 
 
class ThreadPool {
public:
    ThreadPool(size_t);
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args)
        -> std::future<typename std::invoke_result<F,Args...>::type>;
    ~ThreadPool();
private:
    // need to keep track of threads so we can join them
    std::vector< std::thread > workers;
    // the task queue
    std::queue< std::function<void()> > tasks;
 
    // synchronization
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};
 
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
    : stop(false)
{
    for (size_t i = 0; i < threads; ++i)
        workers.emplace_back(
            [this]
            {
                for (;;)
                {
                    std::function<void()> task;
 
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,
                            [this] { return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty())
                            return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
 
                    task();
                }
            }
            );
}
 
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::invoke_result<F,Args...>::type>
{
    using return_type = typename std::invoke_result<F,Args...>::type;
 
    auto task = std::make_shared< std::packaged_task<return_type()> >(
        std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
 
    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
 
        // don't allow enqueueing after stopping the pool
        if (stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
 
        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
}
 
// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& worker : workers)
        worker.join();
}
 
 
 
//...в реализации...
int coresCount=8;
std::vector<std::future<int>> thr(coresCount);
//...
thr[treadN]=pool.enqueue(...);
//...
Код взят отсюда.

Добавлено через 8 минут
В конструкторе своего класса
C++
1
ThreadPool pool (8);
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
02.05.2023, 21:06
Ответы с готовыми решениями:

Ручное управление количеством потоков в ThreadPool
Картина такая: есть некое приложение, которое может внутри себя наплодить до 1к потоков через Task.Start, для общения с сетевыми девайсами....

Сканирование портов в несколько потоков с помощью ThreadPool
Мне необходимо создать приложение в котором вводится определенное количество потоков которые сканируют порты. Как создать? ...

Ограниченное количество одновременно работающих потоков без ThreadPool
К делу. Так это работает в ThreadPool ThreadPool.QueueUserWorkItem(clientObject.Process); ThreadPool.SetMaxThreads(3, 3); Как это...

55
270 / 202 / 30
Регистрация: 26.11.2022
Сообщений: 879
03.05.2023, 15:07
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от bedvit Посмотреть сообщение
Загрузить в поток функцию, получить отметку, что функция отработала, заморозить поток до следующей функции/задачи (количество потоков всегда фиксированное число).
Это и называется очередь - она на самом деле очень простая и в ней нет ничего лишнего.

что качается сравнения parallel_launch и threadpool - вы не могли бы показать код как вы это запускаете и где делается замер времени?
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 18:13  [ТС]
При закрытии приложения, вызывается деструктор моего класса, в котором вызывается деструктор thread_pool и на t.join() процесс зависает. В чем причина может быть, закрыт родительский поток? как можно побороть данную ошибку?

C++
1
2
3
4
5
6
7
    ~thread_pool()
    {
        assert(!task);
        pass(0);
        for (auto& t : pool)
            t.join(); //зависает при закрытии родительского приложения и вызове деструктора класса
    }
Цитата Сообщение от Aledveu Посмотреть сообщение
показать код как вы это запускаете и где делается замер времени?
В своей библиотеке, которая, вызывается из другого приложения (COM библиотека), в общем проще с нуля тест сделать, чем оттуда выдрать

Добавлено через 20 минут
Цитата Сообщение от bedvit Посмотреть сообщение
и на t.join() процесс зависает
t.joinable() - не помог. И в нормально случае и в случае зависания показывает true.
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
03.05.2023, 18:29
Цитата Сообщение от bedvit Посмотреть сообщение
В чем причина может быть, закрыт родительский поток?
Да много в чем, я ж писал, что это гомнокод, только для примера. Код не безопасен по исключениям. Если какой-то поток завершился с исключением, то счетчики барьеров не будут декрементированы (drop). Так же метод parallel_launch не реентрабельный, т.е. нельзя вызывать его из задачи, а так же конкурентно.

Добавлено через 12 минут
Цитата Сообщение от zayats80888 Посмотреть сообщение
Если какой-то поток завершился с исключением
Хотя в этом случае должно падать всё приложение...
Скорее всего конкурентный запуск parallel_launch виноват.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 18:48  [ТС]
std::jthread::request_stop - не помог.
Цитата Сообщение от zayats80888 Посмотреть сообщение
что это гомнокод,
а где можно посмотреть нормальную реализацию? Показанный код работает, все норм. Проблема при закрытии класса, причем когда класс закрывается (в dll) при работающем родительском приложении. все норм. Когда приложение закрывается, вызывая деструктор уже в dll, тогда и появляется эта ошибка.

Т.е. Ошибка возникает, когда ~thread_pool() вызывается при закрытии родительского приложения.
При работающем родительском приложении ~thread_pool() отрабатывает нормально.
Цитата Сообщение от zayats80888 Посмотреть сообщение
конкурентный запуск parallel_launch виноват.
что нужно поправить, если это действительно он.


И еще, поделитесь мнением, возможно с std::jthread будет проще реализовать такой же механизм?
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
03.05.2023, 18:56
Цитата Сообщение от bedvit Посмотреть сообщение
std::jthread::request_stop - не помог.
1) У меня там нет jthread и логики прерывания через stop token.
2) А еслиб был, его вручную джойнить не пришлось бы.
Цитата Сообщение от bedvit Посмотреть сообщение
Проблема при закрытии класса, причем когда класс закрывается (в dll) при работающем родительском приложении. все норм. Когда приложение закрывается, вызывая деструктор уже в dll, тогда и появляется эта ошибка.
Значит тут собака зарыта. А можно подробнее? В чем разница технически между этими вариантами?
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 19:11  [ТС]
Это я заменил на jthread для решения проблемы, но не вышло. Сможете подсказать, как через стоп токен написать (еще не пользовался)?

thread_pool() я добавил в свой клас. В конструкторе своего класса я запускаю thread_pool() с нужным количеством потоков, в деструкторе своего класса ~thread_pool() .
Мой класс, это один из классов в библиотеке СОМ.dll.
COM.dll подключена, в данный момент к Excel. Через раннее связывание (кстати, нужно протестировать через позднее, на эту ошибку)
Пока Excel открыт, в использовании моего класса и thread_pool() не возникает проблем. В VBA клас создаю, удаляю, в дебагере библиотеки вижу что все конструкторы и деструкторы этих классов (моего и thread_pool) отрабатывают штатно.
При закрытии Excel, визуально окно закрывается, вызывается деструктор моего класса (отрабатывает штатно, если не вызывать деструктор thread_pool), а вот деструктор thread_pool зависает в цикле вектора потоков на первом же t.join();
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
03.05.2023, 19:22
Цитата Сообщение от bedvit Посмотреть сообщение
COM.dll подключена, в данный момент к Excel. Через раннее связывание (кстати, нужно протестировать через позднее, на эту ошибку)
Я с COM не подскажу, не пользовался этой технологией и не знаю, как она с многопоточкой увязывается и как вообще реализовано.
Цитата Сообщение от bedvit Посмотреть сообщение
При закрытии Excel, визуально окно закрывается, вызывается деструктор моего класса (отрабатывает штатно), а вот деструктор thread_pool зависает в цикле вектора потоков на первом же t.join();
Возможно при таком завершении деструкторы вызываются в "вакууме", например, все остальные потоки замораживаются и/или эти вызовы делаются в DllMain в ветке DLL_PROCESS_DETACH, что может вызывать эти проблемы (там вредно использовать WaitForSingleObject, который скрывается за join).
Цитата Сообщение от bedvit Посмотреть сообщение
Это я заменил на jthread для решения проблемы, но не вышло. Сможете подсказать, как через стоп токен написать (еще не пользовался)?
Это не поможет (и роли тут не играет). Проблема с увязкой нюансов платформы. В качестве временного решения попробуйте детачить потоки, вместо присоединения.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 19:30  [ТС]
Возможно ли принудительно убить потоки, не используя t.join()? Все расчеты уже сделаны, ничего ждать и возвращать не нужно.
Если вызовы делаются в DllMain в ветке DLL_PROCESS_DETACH, здесь есть лекарство?
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
03.05.2023, 19:35
Цитата Сообщение от bedvit Посмотреть сообщение
Возможно ли принудительно убить потоки, не используя t.join()? Все расчеты уже сделаны, ничего ждать и возвращать не нужно.
Можно, через std::thread::naitive_handle. Но для начала попробуйте просто:
Цитата Сообщение от zayats80888 Посмотреть сообщение
В качестве временного решения попробуйте детачить потоки, вместо присоединения.
Добавлено через 58 секунд
Цитата Сообщение от bedvit Посмотреть сообщение
В VBA клас создаю, удаляю
А в VBA нет концепции RAII?
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 19:51  [ТС]
Принципиально СОМ в данном случае не отличается от обычной dll. Есть разница в раннем или позднем связывании (что-то вроде, статистическте подключение и динамическое, но это влияет на конструкторы и деструкторы классов, помоему так, при раннем связывании конструктор и деструктор у меня срабатывает два раза (два экземпляра) при открытии Excel/закрытии и при использовании класса в vba, при позднем связывании надо проверить, но в теории один раз - при использовании класса в коде - динамическое подключение)

Добавлено через 14 минут
"А в VBA нет концепции RAII?" - Нет. Можно руками создать объект и уничтожить, но никто этим не занимается. В конце процедуры (после щавершения) VBA сам их закроет.
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
03.05.2023, 19:52
Цитата Сообщение от bedvit Посмотреть сообщение
Принципиально СОМ в данном случае не отличается от обычной dll.
Обычно я сам управляю ресурсами, а тут это делает прослойка COM...

Кстати, а если сделать глобальный пул потоков в dll (статический объект), а ваш класс пусть просто хранит ссылку на него (он же вам все равно только один нужен) что-то изменится?
0
270 / 202 / 30
Регистрация: 26.11.2022
Сообщений: 879
03.05.2023, 20:41
Цитата Сообщение от bedvit Посмотреть сообщение
а вот деструктор thread_pool зависает в цикле вектора потоков на первом же t.join();
join - waits for the thread to finish its execution (public member function)

вы неправильно организовали завершение пула потоков.
надо выставить флаг чтобы потоки его проверяли и сами завершались.
при этом держать атОмный счётчик числа работающих потоков.
вот псевдо код на С )
C
1
2
3
4
5
6
7
8
9
10
11
12
MTWQ_AtomicStore_uint( &Queue->ShutdownFlag, 1 );
    while ( MTWQ_AtomicLoad_uint( &Queue->NumActiveThreads ) > 0 )
    {
        WorkQueue_AddWork( Queue, 0, 0 );
    }
 
    for ( unsigned int i = 0; i < Queue->NumThreads; i++ )
    {
        pthread_join( Queue->ThreadControlBlocks[ i ].Thread, 0 );
    }
 
    return;
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
03.05.2023, 20:47  [ТС]
В СОМ. dll так же нужно управлять ресурсами, она написана на С/С++.Это на VBA не нужно, но это пользователям, а не мне. Глобальный пул потоков идея неплохая, но ничем не отличается от пула в классе. Создавать и разрушать все равно надо и это приводит обратно к вопросу с возникающей ошибкой при закрытии Excel. Ну и плюс, в теории, если библа лежит в сети и подключается на разных ПК, у каждого ПК свое количество ядер, и тогда целесообразно в экземпляре класса создавать пул в зависимости от окружения.
0
03.05.2023, 21:03

Не по теме:

Цитата Сообщение от bedvit Посмотреть сообщение
Глобальный пул потоков идея неплохая, но ничем не отличается от пула в классе.
Не совсем. Я точно не скажу, но возможно как и с main, деструкторы глобальных объектов вызываются уже после DllMain. Это я из интереса просил проверить, но раз поведение не изменилось, то и ладно.

0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
04.05.2023, 10:11  [ТС]
Отчет по ошибке:
1. Эта же ошибка возникает в ThreadPool (в топике), отчего возникает вопрос в локальности проблемы или не оптимальности кода и в ThreadPool
2. Замена t.join() на t.detach() - приводит к критической ошибке, приложение падает
3. Замена t.join() на TerminateThread(t.native_handle(), 0) - при закрытии приложения отрабатывает, но при вызове деструктора класса в VBA подвешивает приложение.
4. Замена t.join() на DWORD d = 0; GetExitCodeThread(t.native_handle(), &d); ExitThread(d); - закрывает приложение

zayats80888, я заметил, что thread_pool создает на 1 поток меньше? к примеру, если ставим в деструктор 4 , создается 3 потока. 4й поток считается - это главный? может в этом причина? Может надо создавать 4 потока?
0
270 / 202 / 30
Регистрация: 26.11.2022
Сообщений: 879
04.05.2023, 10:22
у вас зависает потому что при вызове join главный поток ждёт завершения потока - а он у вас ждёт в condition.wait
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
04.05.2023, 10:23  [ТС]
Aledveu, что нужно исправить в коде?
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
04.05.2023, 13:51
Цитата Сообщение от bedvit Посмотреть сообщение
4й поток считается - это главный?
Да.
Цитата Сообщение от bedvit Посмотреть сообщение
может в этом причина? Может надо создавать 4 потока?
Нет. Еще раз повторю, проблема в том, что код не соответствует требованиям COM. Это обширная тема, в которой я не разбираюсь и в которую я погружаться не собираюсь, все таки это в ваших интересах.
Вот одна из возможных причин:
https://learn.microsoft.com/en... ls/dllmain
When handling DLL_PROCESS_DETACH, a DLL should free resources such as heap memory only if the DLL is being unloaded dynamically (the lpvReserved parameter is NULL). If the process is terminating (the lpvReserved parameter is non-NULL), all threads in the process except the current thread either have exited already or have been explicitly terminated by a call to the ExitProcess function, which might leave some process resources such as heaps in an inconsistent state. In this case, it is not safe for the DLL to clean up the resources. Instead, the DLL should allow the operating system to reclaim the memory.
1
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
04.05.2023, 14:55  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
Вот одна из возможных причин:
Спасибо за инфо, сегодня как раз с утра разбирался с DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH.
Проблему решил: проблема была не в Excel. Excel в действительности и при раннем связывании и при позднем пользуется dll, только в сеансе выполнения кода VBA.
А то, что я описывал выше (запускался конструктор при открытии Excel) - это открывалась другая надстройка, которая тоже использует СОМ. Дело было в ней.
Если кратко создавался процесс через CoCreateInstance(), а убивался автоматом при закрытии Excel и соответственно надстройки - умным указателем CComPtr <IDispatch>.
Я не стал копаться в процессах закрытия Excel, СОМ, надстройки и что происходит в это время с потоками, а просто сократил время жизни CComPtr до закрытия приложения.
Спасибо за thread_pool, взял на вооружение.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
12.05.2023, 16:32  [ТС]
zayats80888, есть ли у вас примеры простых parallel_launch, с использованием std::jthread и токенов? Возможно поделитесь информацией, где можно посмотреть, с разбором на пальцах? В гугле только поделки, не внушающие доверия или серьезные Thread Pool c несколькими очередями. Мне же нужен, очень простой пример и главное быстрый. Хочу понять, есть ли профит от С++20 в плане многопоточности и в сравнении с std::thread.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
12.05.2023, 16:32

ThreadPool и использование параллельных классов: распределение потоков по ядрам ЦП
задаю в переменную кол-во потоков и мне надо чтобы каждый поток выполнялся на ядре. Например 2х ядерный процессор. Задаю 4 потока и 2...

Обработка элементов в несколько потоков: ThreadPool или еще варианты?
Во общем дело такое: Есть listview в нем N итемов, мне нужно пройтись по каждому взять данные каждого итема и записать их в файл, и хочу...

Смысл значений ThreadPool.SetMaxThreads и ThreadPool.SetMinThreads
Товарищи объясните пожалуйста из каких соображений назначать значения ThreadPool.SetMaxThreads и ThreadPool.SetMinThreads

Как оптимизировать перемножние результатов массива задач?
Привет всем! Задача следующая: имею массив - Task&lt;BigInteger&gt; нужно перемножить все результаты. На данный момент это решается...

Алгоритм оптимального распараллеливания задач на несколько потоков
Имеем 4-ядерный процессор и 100 задач. Задачи разные по сложности - могут выполняться от 1 до 10 секунд каждая. Необходимо равномерно...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Алиса нашла кучу ошибок компиляции и запуска в проекте, который без проблем компилировался и запускался)))
anaschu 30.06.2026
Я пока посмеюся, но завтра проверю. А вообще интерсно. Дал алисе файл, в котором точно нет ошибок компиляции и запуска, и попросил их найти. Нашла кучу))) Критические ошибки, мешающие компиляции и. . .
сукцессия 16. Общий обзор, в основном что бы другие ии поняли
anaschu 29.06.2026
# Передаточный документ: модель микоризной сукцессии (для нового чата) Этот документ предназначен для того, чтобы новый чат Claude мог продолжить работу без необходимости заново разбираться в. . .
сукцессия 15 неявная схема
anaschu 29.06.2026
Алиса Калибровка параметров симбиотической модели: технический обзор Содержание: Введение Постановка проблемы Технические аспекты реализации Процесс внедрения изменений
сукцессия 14. Обновленная схема модели
anaschu 28.06.2026
ГЛОБАЛЬНАЯ ОПИСАТЕЛЬНАЯ СПЕЦИФИКАЦИЯ ЭКОСИСТЕМНОЙ МОДЕЛИ «SOIL CHEMISTRY & MYCORRHIZA 2. 0» https:/ / ibb. co/ NnkGpfMd Представленная интегрированная схема описывает непрерывную нелинейную. . .
сукцессия 13. Питон модель трехзонного мицелия, пока что в основном арбускулярного
anaschu 28.06.2026
## Разработка агентной модели микоризной сукцессии: от выявления артефактов к созданию комплексной системы ### Аннотация Представлено исследование по разработке агентной модели микоризной. . .
сукцессия 12. краткий список проверок модели перед запуском.
anaschu 27.06.2026
Скрытые отказы в моделях систем динамики (SD-models) экологических систем: два случая из практики Контекст Разбирался прототип модели систем динамики (SD-модели) микоризной сукцессии: пять. . .
Сукцессия 11. Проверка орудий перед войной: разработка через тестирование
anaschu 27.06.2026
Как не дать модели соврать самой себе: проверки для симуляции микоризной сукцессии Введение Когда вы строите математическую модель живой системы — грибов, растений, почвы — главная опасность. . .
10 сукцессия. Питон код войны грибов и растений
anaschu 27.06.2026
import numpy as np class PlantAgent: def __init__(self, name, strategy, initial_biomass): self. name = name self. strategy = strategy # "greedy" (широколиственные) или. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru