Когда я прочитал исходный код asio, мне любопытно, как asio создавал данные, синхронизированные между потоками, даже неявная цепочка была сделана. Это код в ASIO:asio неявная цепочка и синхронизация данных
io_service :: бежать
mutex::scoped_lock lock(mutex_);
std::size_t n = 0;
for (; do_run_one(lock, this_thread, ec); lock.lock())
if (n != (std::numeric_limits<std::size_t>::max)())
++n;
return n;
io_service :: do_run_one
while (!stopped_)
{
if (!op_queue_.empty())
{
// Prepare to execute first handler from queue.
operation* o = op_queue_.front();
op_queue_.pop();
bool more_handlers = (!op_queue_.empty());
if (o == &task_operation_)
{
task_interrupted_ = more_handlers;
if (more_handlers && !one_thread_)
{
if (!wake_one_idle_thread_and_unlock(lock))
lock.unlock();
}
else
lock.unlock();
task_cleanup on_exit = { this, &lock, &this_thread };
(void)on_exit;
// Run the task. May throw an exception. Only block if the operation
// queue is empty and we're not polling, otherwise we want to return
// as soon as possible.
task_->run(!more_handlers, this_thread.private_op_queue);
}
else
{
std::size_t task_result = o->task_result_;
if (more_handlers && !one_thread_)
wake_one_thread_and_unlock(lock);
else
lock.unlock();
// Ensure the count of outstanding work is decremented on block exit.
work_cleanup on_exit = { this, &lock, &this_thread };
(void)on_exit;
// Complete the operation. May throw an exception. Deletes the object.
o->complete(*this, ec, task_result);
return 1;
}
}
в его do_run_one, то разблокировка мьютекса все, прежде чем выполнить обработчик. Если есть неявная цепочка, обработчик не будет выполняться одновременно, но проблема следующая: thread A Запустите обработчик, который модифицирует данные, а поток B запускает следующий обработчик, который считывает данные, которые были изменены потоком A. Без защиты мьютекса , как поток B видел изменения данных, сделанные потоком A? Отпирание мьютексов перед выполнением обработчика не происходит, прежде чем отношения между потоками будут обращаться к данным, к которым обратился обработчик. Когда я иду дальше, выполнение обработчика использовать вещь под названием fenced_block:
completion_handler* h(static_cast<completion_handler*>(base));
ptr p = { boost::addressof(h->handler_), h, h };
BOOST_ASIO_HANDLER_COMPLETION((h));
// Make a copy of the handler so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
Handler handler(BOOST_ASIO_MOVE_CAST(Handler)(h->handler_));
p.h = boost::addressof(handler);
p.reset();
// Make the upcall if required.
if (owner)
{
fenced_block b(fenced_block::half);
BOOST_ASIO_HANDLER_INVOCATION_BEGIN(());
boost_asio_handler_invoke_helpers::invoke(handler, handler);
BOOST_ASIO_HANDLER_INVOCATION_END;
}
что это? Я знаю, что забор кажется примитивом sync, который поддерживается C++ 11, но этот забор полностью написан самим asio. Помогает ли это fenced_block выполнять работу по синхронизации данных?
ОБНОВЛЕНО
После того как я Google и читать this и this, ASIO действительно использовать забор памяти примитив для синхронизации данных в потоках, то есть более чем быстрее разблокировать до обработчика выполнить полное (speed difference on x86). На самом деле ключевое слово Java volatile реализовано путем вставки барьера памяти после записи & перед тем, как прочитать эту переменную, чтобы произойти до отношения.
Если кто-то может просто описать реализацию забора памяти в asio или добавить что-то, что я пропустил или неправильно понял, я его приму.
Можете ли вы рассказать о том, что вы не понимаете, с проблемой нити A и B? Если параллелизм не возникает, потому что обработчики выполняются через цепочку (неявную или явную), то если поток А изменяет X, почему поток B, который работает после потока A, не замечает изменения в X? Мьютекс не дает преимуществ при отсутствии параллелизма. –
Это проблема видимости, и она четко определена в модели памяти Java. У C++ нет такой вещи (хорошо определенной модели памяти), но я думаю, что она такая же из-за кэша потоков, потому что это характер процессора. Изменения в Thread A могут изменять только копию, которая кэшируется в своей области (кеш процессора), но оставила основную память нетронутой. Без синхронизации поток B может увидеть или не увидеть это обновленное значение, это зависит от того, будет ли кэш-память кэша в основной памяти. Но в C++ нет такой вещи, которая называется проблемой видимости, она просто говорит: всякий раз, когда вы пишете или читаете переменную, используйте блокировку – jean