First time using threads in C++. I've been looking at using boost which is very confusing for me. Basically all I'm trying to do is:
Create a worker thread that does some work asynchronously. Continue main thread while work is being done.
When the worker thread is done, fire a callback function with some results that executes in the main thread context.
So something similar to thread handling in C#.
There doesn't seem to be any support for 2. Using an io_service together with an async function, and thereafter using run() on the io_service seems to block the main thread. So not very asynchronous.
I've tried using boost::future as per the example here: Using boost::future with "then" continuations
Here the "then" continuation is done in a separate thread, not the main thread, so not what I'm after. Is there any way to alter this? Using boost::launch::deferred and wait() makes the call synchronous, so that doesn't help either. Same with just using get() on the boost::future construct.
It seems the only option is to create a mutex-locked shared event queue, and just poll it continuously for new data in the main thread?
It's unusual to preempt the main thread in whatever it was doing to start working on the callback. Even in "thread handling in C#" (which is quite a broad subject) the main thread will typically process callbacks when it is processing the thread's message queue.
So typically, the main thread only executes callbacks when it is ready to do so. One way of implementing that is by calling run() on an io_service.
Your main thread can only process one message queue at a time. If your application happens to be a Windows GUI application, then your main thread is already processing a message queue (the windows message queue) and should not perform a blocking function call like run() on an IO service (which is handling another message queue). In such a case, you can decide to write code that wraps your callback in a windows event message and process that.
If you happen to be using Qt, then the answer to this question shows you how you could combine an asio io_service with your message loop (I haven't tried that one).
If your process is not a GUI application, then, since you already seem to be somewhat familiar with asio, you could still use an io_service. In that case however, all functions that the main thread performs (after initialization) should be run as events on that message queue. For example: "Continue main thread" in your question could then be implemented as another callback on the io_service.
Related
The common way to process Asio handlers is to have a thread (or several threads) either polling io_service (i.e. calling io_service::poll()) regularly to run the handlers or using io_service::run(), which blocks the thread until there's work to do, in which case the thread will run the required handlers and either return or go to sleep again.
However, I want to make a system where a thread is not only responsible for running Asio handlers, but also needs to sync up with another thread using a condition variable. Basically, I want the thread to do all of these:
Wake up when there are Asio handlers that need to be processed (i.e. if I call io_service::poll(), one or more handlers will be processed).
Wake up when there is non-Asio work to be done, indicated by my condition variable.
Sleep otherwise.
In other words, I need a way for Asio to signal me that there are handlers ready to execute, without having to busy-wait or continuously poll. Ideally, Asio will somehow signal a thread when work is available, and that thread will in turn wake up my main worker thread, which will process Asio handlers. That worker thread will also be occasionally woken up by yet another thread, and will process other, non-Asio related work.
Is this even feasible, or should I reconsider how I am designing my system?
Normally when using GTK3 callbacks with the gtk_main() function, it is expected that callbacks maybe running in a seperate thread from your programs main thread, and therefore thread safety measures need to be taken to ensure callbacks are thread safe.
However, what if your main thread deals with gtk main events manually using gtk_main_iteration?
// Some processing here
while (gtk_events_pending()) {
gtk_main_iteration();
}
// More processing here
In this situation, are callbacks still run in a separate thread? or are they launched from the main thread?
My program has a lot going on and if it turns out callbacks are called from the main thread in this setup, it would save me quite a bit of thread safety work.
Note: I'm working with GTK3, but I believe this applies to both GTK2 and GTK3
I am reading multicast input using async_receive_from. So the idea is that when I detect a gap, I will notify another helper thread to request/get the gap filling messages. While this is in the works the main thread will continue to receive and queue any incoming messages. This part I can implement. The other thread can use waitforsingleobject and I can pass it the details through shared memory and notify an event to wake it up.
But once it completes it task, how do I get the helper thread to interrupt the async_receive_from in the initiating thread? And when it comes up out of the the read it knows who interrupted so it will then know what to do next?
Why are you using shared memory between threads?
That aside, the mechanism you should use for executing something in the context of the io_service which is managing the socket is post(). You can post any arbitrary event to the io_service, and it will execute in that context. Quite easy really... Because you are calling async_receive_from, it's not blocking, i.e. the io_service can dispatch other events, which is why the post will work.
I was just going over the asio chat server example. My question is about their usage of the io_service.run() function. The documentation for the io_service.run() function says:
The run() function blocks until all work has finished and there are no
more handlers to be dispatched, or until the io_service has been
stopped. Multiple threads may call the run() function to set up a
pool of threads from which the io_service may execute handlers. All
threads that are waiting in the pool are equivalent and the io_service
may choose any one of them to invoke a handler. The run() function
may be safely called again once it has completed only after a call to
reset().
It says that the run function will return, and I'm assuming that when it does return the network thread stops until it is called again. If that is true, then why isn't the run function called in a loop, or at least given its own thread? the io_service.run() function is pretty much a mystery to me.
"until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped"
Notice that you DO install a handler, named handle_accept, that reinstalls itself at each execution. Hence, the io_service.run will never return, at least until you quit it manually.
Basically, at the moment you run io_service.run in a thread, io_services proactor takes over program flow, using the handler's you installed. From that point on, you handle the program based on events (like the handle_accept) instead of normal procedural program flow. The loop you're mentioning is somewhere deep in the scary depths of the asio's proactor ;-).
I've already wasted two days reading documentation of boost::asio
And I still don't know how I could implement blocking select() like function for several sockets using only one thread (using boost framework).
Asynchronous functions of boost::asio return immediately, so there would be a need to put some wait function in main thread until one of the async_read's finishes.
I suspect that this would time consuming, but I'm really restricted by performance requirements.
The io_service object is an abstraction of the select function. Set up your sockets and then call the io_service::run member function from your main thread. The io_service::run function will block until all of the work associated with the io_service instance is completed. You can schedule more work in your asynchronous handlers.
You can also use io_service::run_one, io_service::poll, or io_service::poll_one in place of io_service::run.