Wednesday 22 April 2015

Threading and Empty While Loops

Today I had my first threading issue at work. The stuff I work on is multi-threaded, of course, but a lot of that is magically handled by the framework that we use, so I've never had to explicitly do anything requiring threads, other than to call Thread.sleep() when I want one of my tests to wait for something to execute first. So my experience with threads is very minimal. It wasn't even covered on the Java exam I did, it's in the next one, that Grad Daniel and I are thinking of sitting next year.

So I was writing a unit test at work today, and I wanted to test that something was executing threads one at a time.


while(!threadUpdated);
vs
while(!threadUpdated){ 
System.out.println("Waiting...."); 
}
The gist of what I was trying: I had a condition that was updated by another threads. So my while loop was looping, just waiting for that condition to change. In the first case, I just had an empty while loop (EmptyLoop above), and it just kept looping forever, even though I knew it was supposed to finish running within 10 seconds (all my threads were doing was sleeping for 5 seconds, then updating the condition). In the second case, I had a while loop with a print statement. I had a Stopwatch measuring the time, and it finished running in 10 seconds, which is what I expected.

I couldn't understand why having a print statement, which did nothing other than print the message "Waiting...." was making my test work as expected, when it wasn't modifying the state at all.

So I tried to debug the code to find out what was going on. When I ran it in debug mode, it was working both with the empty while loop, and with the print while loop. WHAT ON EARTH WAS GOING ON?!

Well, the lovely thing with working with threads is that it's hard to track down issues, because by running your code in debug mode and stepping through it slowly, all the other damn threads are doing as they please, and that makes it really difficult to replicate the issue, because if you run it normally, and thread A runs, then B, then C, debugging A might mean that B and C finish while you're busy inspecting A, and so you're not really reproducing the original situation.

As it turns out, what I was missing was the fact that my empty while loop was causing that particular thread to hog the resources (or at least that's my theory). So while it was looping endlessly, it never gave control over to the thread that updates the condition, so the condition never gets updated, and so it keeps looping. When I ran it in debug mode, when it paused for me to inspect it, the condition thread got a chance to run, and so it updated the condition, and the test passed as I was expecting. When I added the print statement, I guess that tiny period where it waits for an I/O process to write to the console meant the updating thread got a chance to execute, so again, the test passed as expected.

As this SO answer explains, busy-waiting is expensive.

And now I know more about threading in Java than I did yesterday.

No comments: