But, I'm not sure whether this is a good idea, because I keep on creating new async_read (things, objects??) how are they being cleaned up?
async_read() is just a free function call. One of its parameters is a completion handler, which you create using bind(). This completion hanlder is put to some internal queue managed by io_service, and when the operation is successfully completed or failed or cancelled, the io_serivce cares to invoke the associated completion handler (for this purpose you have pump io_sevice queue with poll or run methods). After the handler is invoked, it's destroyed. So you don't have to "clean-up" anything here, but you do have to ensure that everything you bind into a handler remains alive until the handler gets invoked.