[asio] multi-threaded io_service::run produce wrong result

Hi, there. At first, sorry for my poor English. I'm a newbie to boost and nowadays I'm trying understand how to use boost::asio in multi-thread environment. I have written a program as below: (I do know it's of no use, I wrote it just for learning) ------------------------------------------------------------ #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <iostream> using std::cout; using std::endl; using namespace boost; using namespace boost::asio; io_service ioserv; mutex m; volatile int c = 0; void add() { lock_guard<mutex> g(m); ++c; } int main() { thread t1(bind(&io_service::run, ref(ioserv))); thread t2(bind(&io_service::run, ref(ioserv))); for(int i = 0; i < 655360; ++i) ioserv.post(&add); t1.join(); t2.join(); cout << c << endl; return 0; } ------------------------------------------------------------ the expected output is 655360. but in fact, nearly everytime when I ran it it gave me a different result ranged from 1 to 655360. I have checked my code, it locks the variable "c" before changing it. and I did join the two threads before I printed the value of "c". If I put the "post loop" before any thread that runs io_service::run starts, it prints 655360 but it's not what I want to add all task before thread starts. I have tried to compile the code with msvc7.1/boost 1.43, msvc9/boost 1.43 and msvc10/boost 1.46, the result is the same. I could find that only one thread works most of the time via process explorer. I thought that it's because t1 and t2 ended while the main thread was posting "add". Then I tried adding "ioserv.run();" before "t1.join();" but it didn't work. And then, if I didn't use t1 and t2, only run "ioserv.run();" after the "for" loop, the result became 655360. What I guessed is that, t1 and t2 ended when the main thread continues to post task to t1 and t2, then these task were lost. Just guessing, the source code of asio is currently hard for me... Is there anything wrong in my program? Hope someone could help me. thanks in advance. the compile and run environment is: 1 core cpu, windows xp pro, boost 1.43, msvc 7.1 1 core cpu, windows xp pro, boost 1.43, msvc 9 2 cores cpu, windows 7 pro, boost 1.46, msvc 10 regards, Leiming

On Mon, Mar 28, 2011 at 08:40:02PM +0800, leiming wrote:
Hi, there.
An io_service will consider itself finished when there is no more pending tasks to perform. That means that if there is sufficient time between the first task and the second task being posted, they may return from run() and end their threads, resulting in the joins being completed. If you want to keep an io_service alive while there are no pending operations, construct an io_service::work object from the io_service. That will keep io_service::run() running until all pending work is finished (which you will indicate by destroying the work object. -- Lars Viklund | zao@acc.umu.se

在 Tue, 29 Mar 2011 02:40:48 +0800,Lars Viklund <zao@acc.umu.se> 写道:
On Mon, Mar 28, 2011 at 08:40:02PM +0800, leiming wrote:
Hi, there.
An io_service will consider itself finished when there is no more pending tasks to perform.
Thank you for your reply!
That means that if there is sufficient time between the first task and the second task being posted, they may return from run() and end their threads, resulting in the joins being completed. I'm also run io_service::run in the main thread after add all jobs but it still produce a wrong answer. It will finish all jobs even if the 2 threads ended because the jobs will run in the main thread after all jobs were added I thought. I don't know why
If you want to keep an io_service alive while there are no pending operations, construct an io_service::work object from the io_service.
That will keep io_service::run() running until all pending work is finished (which you will indicate by destroying the work object.
I have tried and it works!

On Tue, Mar 29, 2011 at 06:49:12AM +0800, leiming wrote:
在 Tue, 29 Mar 2011 02:40:48 +0800,Lars Viklund <zao@acc.umu.se> 写道:
On Mon, Mar 28, 2011 at 08:40:02PM +0800, leiming wrote:
Hi, there.
An io_service will consider itself finished when there is no more pending tasks to perform.
Thank you for your reply!
That means that if there is sufficient time between the first task and the second task being posted, they may return from run() and end their threads, resulting in the joins being completed. I'm also run io_service::run in the main thread after add all jobs but it still produce a wrong answer. It will finish all jobs even if the 2 threads ended because the jobs will run in the main thread after all jobs were added I thought. I don't know why
I do not quite know why, but I would guess that once an io_service has gone into the stopped state, you might have to perform some action to reset it, so that it can be started again. I would not expect any actions you post to it during the stopped-but-not-reset period to have any effect.
If you want to keep an io_service alive while there are no pending operations, construct an io_service::work object from the io_service.
That will keep io_service::run() running until all pending work is finished (which you will indicate by destroying the work object.
I have tried and it works!
Hooray! -- Lars Viklund | zao@acc.umu.se

I do not quite know why, but I would guess that once an io_service has gone into the stopped state, you might have to perform some action to reset it, so that it can be started again.
Yes, io_service::reset() should be called.

在 Wed, 30 Mar 2011 05:56:39 +0800,Lars Viklund <zao@acc.umu.se> 写道:
On Tue, Mar 29, 2011 at 06:49:12AM +0800, leiming wrote:
I do not quite know why, but I would guess that once an io_service has gone into the stopped state, you might have to perform some action to reset it, so that it can be started again. I would not expect any actions you post to it during the stopped-but-not-reset period to have any effect.
Oh, I try running ioserv.reset(); ioserv.run(); after the two joins and this time it works as I excepted. Thank you very much. By the way, I see this statement in boost::asio::io_service::reset document: This function must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_service being stopped or running out of work. I didn't notice this before... orz
Hooray!
participants (3)
-
Igor R
-
Lars Viklund
-
leiming