Я написал небольшой класс пула потоков и адаптировал его к рекомендациям, данным pschill (https://codereview.stackexchange.com/questions/229613/c-standard-thread-threadpool).). Теперь я ставлю в очередь несколько задач, пока поток не сможет их обработать. Задача состоит из enum class State
, std::mutex
(для состояния) и std::packaged_task<R(Args...)>
. Задача создается вызывающим экземпляром (см. Пример кода), а время жизни гарантируется и проверяется. Как только поток готов, задача обрабатывается.здесь все прекрасно работает в каждой конфигурации сборки.
Выполнение сборки Debug x64 (последняя версия VCpp) дало мне ожидаемое значение 4
. Использование любой другой конфигурации приводит к «случайному» значению....
Я проверил время жизни каждого задействованного объекта, как и ожидалось.
Выполнение задачи
{
// May unnecessary but added due to previous errors
std::lock_guard<std::mutex>
m_state = State::INPROGESS;
}
m_task(args...);
{
std::lock_guard<std::mutex> lock(m_statelock);
m_state = State::DONE;
}
Задача очереди
template <typename R, typename ... Args>
void addTask(Task<R, Args...>& task, Args ... args) {
// m_tasks is a std::deque<std::function<void()>>
std::lock_guard<std::mutex> lock(m_tasklock);
m_tasks.emplace_back([&]() -> void {
task.execute(args...);
});
// Has own mutex
m_task_avaiable.notify_one(); // std::condition_variable
}
Процедура объекта потока
[&]() -> void {
while (true) {
while (tpool.m_tasks.size() > 0) {
{
// Added due to crash with Release-config at asgining
// atomic<State> in Task
std::lock_guard<std::mutex> lock(statelock);
state = State::INPROGRESS;
}
std::function<void()> job;
{
std::lock_guard<std::mutex> lock(tpool.m_tasklock);
if (tpool.m_tasks.size() > 0) {
job = std::move(tpool.m_tasks[0]);
tpool.m_tasks.pop_front();
}
}
if (job) { job(); }
}
{
std::lock_guard<std::mutex> lock(statelock);
state = State::IDLE;
}
tpool.m_task_avaiable.wait(lock);
{
std::lock_guard<std::mutex> lock(statelock);
if (state == State::STOP) {
return; // To stop thread and make it joinable
}
}
}
}
Main
frm::util::ThreadPool pool(2);
frm::util::Task<int, int, int> t1([](int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Yey!...\n";
return a + b;
});
pool.addTask(t1, 2, 2);
// Direct access to member std::packaged_task<...> just for debugging
std::cout << t1.m_task.get_future().get() << '\n';
Если онопомогает, дамп объекта задачи (x32), показывающий, что было запрошено будущее (байт 61):
class std::mutex 48 Byte
1 0x02 0x00 0x00 0x00
5 0xbc 0x78 0x9f 0x0f
9 0x00 0x00 0x00 0x00
13 0x00 0x00 0x00 0x00
17 0x00 0x00 0x00 0x00
21 0x00 0x00 0x00 0x00
25 0x00 0x00 0x00 0x00
29 0x00 0x00 0x00 0x00
33 0x00 0x00 0x00 0x00
37 0x00 0x00 0x00 0x00
41 0xff 0xff 0xff 0xff
45 0x00 0x00 0x00 0x00
struct std::atomic<enum frm::util::Task<int,int,int>::State> 4 Byte
49 0x02 0x00 0x00 0x00
class std::packaged_task<int __cdecl(int,int)> 12 Byte
53 0x50 0xbd 0x15 0x01
57 0x00 0x00 0x00 0x00
61 0x01 0x00 0x00 0x00
Здесь есть какие-либо проблемы с std::future<T>
, std::shared_future<T>
или std::packaged_task<T>
в VCpp19 или я что-то пропустил?
Я могу показать больше кода, но из-за правил и рекомендаций SO я скопировалкак можно меньше.
Может также помочь:
Перед использованием addTask(Task<R, Args...>& task, Args ... args)
addTask(Task<R, Args...>* task, Args ... args)
вызвала ошибку (не в отладке x64), если была добавлена задержка, например:
// Direct access to member std::packaged_task<...> just for debugging
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << t1.m_task.get_future().get() << '\n';