[ASIO] Using boost::asio::thread_pool with sockets without boost::asio::io_context
Hello, One strategy to load-balance I/O processing across multiple threads is to use a single io_context object for multiplexing and to call io_context::run from all threads in the thread pool. This can be set up by, for example, defining a type which holds a vector of threads, all of which are calling io_context::run. My first question is: is this approach of calling io_context::run from several threads somewhat equivalent to using boost::asio::thread_pool as the default execution context and not (explicitly, at least) creating a boost::asio::io_context object at all? Somewhat more specifically, is it safe to define TCP sockets/acceptors/etc. with execution contexts set to a boost::asio::thread_pool instead of io_context, and will that work as expected (i.e., as a single io_context object scheduling tasks out to several threads calling io_context::run would)? -Ani
On Thu, 9 Jul 2020 at 16:02, Aniruddh Agarwal via Boost-users < boost-users@lists.boost.org> wrote:
Hello,
One strategy to load-balance I/O processing across multiple threads is to use a single io_context object for multiplexing and to call io_context::run from all threads in the thread pool. This can be set up by, for example, defining a type which holds a vector of threads, all of which are calling io_context::run. My first question is: is this approach of calling io_context::run from several threads somewhat equivalent to using boost::asio::thread_pool as the default execution context and not (explicitly, at least) creating a boost::asio::io_context object at all?
It's not equivalent because in the case of one thread per io_context, the completion handler of asynchronous operations will always be invoked on a known thread. Therefore concurrency protection is not a concern. If you use the executor of a thread_pool, there is no guarantee which thread will invoke the completion handler, and no guarantee that two completion handlers will not touch the same user object. This argues for the use of asio::strandasio::thread_pool::executor_type as the executor to be associated with the completion handler (or set as the default executor of related io objects).
Somewhat more specifically, is it safe to define TCP sockets/acceptors/etc. with execution contexts set to a boost::asio::thread_pool instead of io_context, and will that work as expected (i.e., as a single io_context object scheduling tasks out to several threads calling io_context::run would)?
It will work. You will need to add concurrency protection, and it will be less efficient. From your line of questioning I don't think it will work as you are expecting or hoping. Is there a use case or is this a question out of curiosity?
-Ani _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212
On Jul 9, 2020, at 10:49 AM, Richard Hodges via Boost-users
It's not equivalent because in the case of one thread per io_context, the completion handler of asynchronous operations will always be invoked on a known thread. Therefore concurrency protection is not a concern.
If you use the executor of a thread_pool, there is no guarantee which thread will invoke the completion handler, and no guarantee that two completion handlers will not touch the same user object. This argues for the use of asio::strandasio::thread_pool::executor_type as the executor to be associated with the completion handler (or set as the default executor of related io objects).
It seems that my original email was a bit misleading and implied that I was comparing (many threads, many asio::io_context objects) with asio::thread_pool. In fact, I’m trying to compare (many threads, one asio::io_context object) with asio::thread_pool. From what you’ve said above, it looks like (many threads, one asio::io_context object) and asio::thread_pool are logically equivalent. Can you speak to any performance differences between these (or point to reference material which touches on this)?
Is there a use case or is this a question out of curiosity?
Mostly out of curiosity. I’ve found myself repeatedly writing code where a single asio::io_context is dishing out tasks to many threads, and am wondering if asio::thread_pool is a drop-in replacement for manually setting up a thread pool and calling asio::io_context::run from each thread. -Ani
On Thu, 9 Jul 2020 at 18:01, Aniruddh Agarwal via Boost-users < boost-users@lists.boost.org> wrote:
It's not equivalent because in the case of one thread per io_context,
On Jul 9, 2020, at 10:49 AM, Richard Hodges via Boost-users < boost-users@lists.boost.org> wrote: the completion handler of asynchronous operations will always be invoked on a known thread. Therefore concurrency protection is not a concern.
If you use the executor of a thread_pool, there is no guarantee which
thread will invoke the completion handler, and no guarantee that two completion handlers will not touch the same user object. This argues for the use of asio::strandasio::thread_pool::executor_type as the executor to be associated with the completion handler (or set as the default executor of related io objects).
It seems that my original email was a bit misleading and implied that I was comparing (many threads, many asio::io_context objects) with asio::thread_pool. In fact, I’m trying to compare (many threads, one asio::io_context object) with asio::thread_pool. From what you’ve said above, it looks like (many threads, one asio::io_context object) and asio::thread_pool are logically equivalent.
From the soon to be released boost 1.74 docs, the executor_type of io_context and thread_pool are the same type: typedef basic_executor_type< std::allocator< void >, 0 > executor_type; , so it seems that it is intended that they will have the same behaviour.
Can you speak to any performance differences between these (or point to reference material which touches on this)?
I cannot point you to material offhand, but you have reminded me that I read this *somewhere*. I seem to remember something about there needing to be something that's calling epoll/select/pselect in a loop. But now you've got me wondering if I am mistaken. Of course if your worker thread is just making a blocking call to run(), that's where the io loop will happen. Your handlers will be executed within the context of the io_context::run which will happen on a thread associated with the thread_pool.
Is there a use case or is this a question out of curiosity?
Mostly out of curiosity. I’ve found myself repeatedly writing code where a single asio::io_context is dishing out tasks to many threads, and am wondering if asio::thread_pool is a drop-in replacement for manually setting up a thread pool and calling asio::io_context::run from each thread.
I suppose this could save you a few lines of code.
-Ani
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212
participants (2)
-
Aniruddh Agarwal
-
Richard Hodges