У меня есть код, который отправляет задачи в asio io_service
объект для удаленной обработки. Насколько я могу судить, код ведет себя правильно, но, к сожалению, я не очень разбираюсь в упорядочении памяти, и я не уверен, какие порядки памяти следует использовать при проверке атомарных флагов для обеспечения оптимальной производительности.
//boost::asio::io_service;
//^^ Declared outside this scope
std::vector<std::atomic_bool> flags(num_of_threads, false);
//std::vector<std::thread> threads(num_of_threads);
//^^ Declared outside this scope, all of them simply call the run() method on io_service
for(int i = 0; i < num_of_threads; i++) {
io_service.post([&, i]{
/*...*/
flags[i].store(true, /*[[[1]]]*/);
});
}
for(std::atomic_bool & atm_bool : flags) while(!atm_bool.load(/*[[[2]]]*/)) std::this_thread::yield();
В общем, я хочу знать, что я должен заменить [[[1]]]
а также [[[2]]]
?
Если это помогает, код функционально похож на следующее:
std::vector<std::thread> threads;
for(int i = 0; i < num_of_threads; i++) threads.emplace_back([]{/*...*/});
for(std::thread & thread : threads) thread.join();
За исключением того, что мой код поддерживает потоки во внешнем пуле потоков и отправляет им задачи.
Вы хотите установить случается, перед тем связь между потоком, устанавливающим флаг, и потоком, который видит, что он установлен. Это означает, что как только поток увидит, что флаг установлен, он также увидит результаты всего, что сделал другой поток перед его установкой (иначе это не гарантируется).
Это может быть сделано с использованием семантики release-receive:
flags[i].store(true, std::memory_order_release);
// ...
while (!atm_bool.load(std::memory_order_acquire)) ...
Обратите внимание, что в этом случае может быть удобнее использовать блокирующий семафор на уровне ОС, чем ожидание вращения в массиве флагов. Если это не удастся, все равно будет немного эффективнее использовать количество выполненных задач, а не проверять массив флагов для каждой из них.
Других решений пока нет …