views:

274

answers:

7

How can I prove that multithreading is working in my C# programs? This is for a testing requirement. For example, I'm going to have to add some locking to a logger class (yes I know, I shouldn't have written my own logging class), and I need a test case to prove that the change will work.

+1  A: 

You just can't :) It's all depends on timing and it might blow up at any time. You have to mentally check every possible situation and that is the only way to go. That's why a lot of developers think multithreading is impossible to get right.

vava
+1, it's practically impossible to test all possible scenarios. If you always use locks to synchronize access to shared data it should be OK even if it is not the best solution from performance point of view.
Darin Dimitrov
Using CHESS will allow you to actually test that your locking is correct - it runs your threading unit tests using every possible code path. Watch the video about CHESS here: http://channel9.msdn.com/posts/Peli/Getting-started-with-CHESS-in-Visual-Studio-2008/
garethm
A: 

This post has a nice discussion on unit testing multi-threaded applications.

Matt
A: 

You actually can't. You can, however, write some debug-time code (start of routine, end of routine, special actions routine takes, ...) that writes to a console, so you can see that routines run at the same time.

Filip P.
+7  A: 

If you want to test that your locking code is correctly synchronizing access to your log(s), you need to construct tests that guarantee contention. This may require you to refactor your code so that you can inject a mock log writer class that can hold the log's lock for arbitrary periods of time.

This is a broad topic, and you can find several related questions on StackOverflow, which are all worth reading:

CHESS is a framework under development for identifying "assertion violations, deadlocks, livelocks, data-races, and memory-model errors." I haven't actually used this, but it looks like it might be extremely helpful.

Jeff Sternal
You'll definitely want to look into CHESS.
garethm
+5  A: 

Well, this may sound wrong but the truth is you can't prove multi-threaded behavior with unit-tests. Having said that, you can gain some confidence in the code with testing and over time it might actually present an issue.

<rant> Multi-threaded code is the bane of my existence in many a project. Often people/developers do not have the expertise required to do a good job. Bugs often go unnoticed for long periods of time before anyone sees it in the wild, and then you can't reproduce the issue to identify whats going on. Further, attempting to 'fix' broken multi-threaded code via debugging is often not a viable approach. </rant>

Anyway, go ahead and test it, there is no harm in doing that much and it's easy enough to do. Just fire up N number of threads, have them all wait on a ManualRestEvent, and then call your api in a tight loop a couple of hundred-thousand times :). But first I would recommend everyone on your team do a code review. Walk every line of code thinking about it executing in parallel. Ask yourself:

  1. Do I really need this lock()?
  2. What's the least amount of code that MUST be in the lock()?
  3. Can I make this object/state immutable and avoid the locking?
  4. Is there any way for a caller to have code execute inside the lock?
  5. Review all members accessed and changed inside a lock for 'volatile'?
  6. Are you using System.Threading.Thread.MemoryBarrier correctly?
  7. If multiple locks are involved are they always obtained in the same order?
  8. [wiki add here]
csharptest.net
+1, this is great advice apart from the first statement. :) You *can* prove specific things about the behavior of multithreaded code in unit tests (for example, you can prove that a lock is being respected by all synchronized methods, among other things). What you cannot do with unit tests is prove that your multithreaded code is bug-free - but that's not what unit tests are for.
Jeff Sternal
A: 

Thread.Sleep. If you're suffering from race conditions with multithreading a well placed Thread.Sleep can increase the size of the race condition making it easier to reproduce.

WriteA();
// potential race condition as another bit of code might read all the state
// and only get A in their read.
WriteB();

to

WriteA();
Thread.Sleep(60000);
WriteB();

Then you can write code that reproduces the problem. Then you can write code that fixes the problem. Then you can assert that your fix works. Profit!

Quibblesome
A: 

Another thread posted a related answer, using Microsoft's CHESS program.

csharptest.net