Boost logo

Boost Users :

Subject: Re: [Boost-users] [asio] Using io_service for synchronous tcp accept + custom tasks
From: Aaron Levy (aaron.levy_at_[hidden])
Date: 2015-02-04 02:38:15


04.02.2015, 06:22, "Gavin Lambert" <gavinl_at_[hidden]>:
> On 2/02/2015 02:52, Aaron Levy wrote:
>>> šio_service svc;
>>> šio_service::work work(svc);
>>>
>>> šthread t1( [&svc]() { svc.run(); } );
>>> šthread t2( [&svc]() { svc.run(); } );
>>> šthread t3( [&svc]() { svc.run(); } );
>>>
>>> šendpoint ep(ip::tcp::v4(), port);
>>> šacceptor acceptor(svc, ep);
>>>
>>> šwhile (true) {
>>> ššššshared_ptr<socket> sock(new socket(svc));
>>> ššššacceptor.accept(*sock);
>>>
>>> ššššsvc.post( [sock]() { š/* do stuff on sock here */ });
>>> š}
>>>
>>> šIs this way of using io_service for accepting tcp connections and
>>> šalso as a thread pool for serving connected clients valid or could
>>> šI hit some undefined behavior.
>
> A little of both. šIn general you can post whatever jobs you like to an
> io_service (including things that aren't I/O -- it's a great generic
> thread pool), but when multiple threads are running the service any one
> of those threads can end up running the job / handling the callback.
>
> Most of the io objects (eg. sockets), and indeed most other objects, are
> not intended for a single instance to be used concurrently from multiple
> threads. šYou can prevent this either by ensuring that only a single
> operation is "in flight" on a single object at a time (implicit strands)
> or that operations on the same object are explicitly synchronised via a
> strand object, or using some other mechanism (eg. locks), although the
> latter is less preferred.
>
> In the code above, you should be fine with regard to acceptor vs. sock,
> since you're only playing with one at a time. šBut you'll need to be
> careful if doing multiple operations on sock.
>
> Also, I could be wrong about this, but I think if you eg. perform a
> blocking read inside your sock job it will tie up a whole thread for the
> duration, which means that you will quickly run out if you get multiple
> connections. šUsing async code should avoid this.
>

Consider an alternative to my example while still using sync I/O.

    io_service svc;
    
    endpoint ep(ip::tcp::v4(), port);
    acceptor acceptor(svc, ep);
    
    boost::thread_group group;
    
    while (true) {
       shared_ptr<socket> sock(new socket(svc));
       acceptor.accept(*sock);
    
       group.create_thread([sock]() { /* do some processing */ });
    }
    group.join_all(); // we never reach here

The problem with the first example would manifest itself with increasing rate of connections - connections would take longer to be accepted and be ready for read / write. With this one, connections would not remain hung up for too long, but subsequent I/O could because there could be too many threads vying for the processors. I'm wondering which one would be preferable.

Are there any specific advantages of sync I/O over async I/O other than simple code?

--
Aaron Levy
aaron.levy_at_[hidden]

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net