Reputation: 28877
this kind of follows on from another question of mine.
Basically, once I have the code to access the file (will review the answers there in a minute) what would be the best way to test it?
I am thinking of creating a method which just spawns lots of BackgroundWorker's or something and tells them all load/save the file, and test with varying file/object sizes. Then, get a response back from the threads to see if it failed/succeeded/made the world implode etc.
Can you guys offer any suggestions on the best way to approach this? As I said before, this is all kinda new to me :)
Following ajmastrean's post:
I am using a console app to test with Debug.Asserts :)
I originally rolled with using BackgroundWorker to deal with the threading (since I am used to that from Windows dev) I soon realised that when I was performing tests where multiple ops (threads) needed to complete before continuing, I realised it was going to be a bit of a hack to get it to do this.
I then followed up on ajmastrean's post and realised I should really be using the Thread class for working with concurrent operations. I will now refactor using this method (albeit a different approach).
Upvotes: 24
Views: 32235
Reputation: 47
For those such as myself who stumble across this thread in the future - the answer given by @Anthony works fantastically, and is basically what I have been using for my (simple) multi-threaded tests. However, using System.Threading.Tasks.Parallel simplifies the following code:
public static void MultiThreadedTest()
{
Thread[] threads = new Thread[count];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(DoSomeWork());
}
foreach(Thread thread in threads)
{
thread.Start();
}
foreach(Thread thread in threads)
{
thread.Join();
}
}
to
public static void MultiThreadedTest()
{
Parallel.For(0, count, {
DoSomeWork();
});
}
What Parallel.For does is perform the loop iterations in parallel. It is also used as an example for the ConcurrentDictionary<TKey,TValue>.AddOrUpdate method (which is also where I saw it as an alternative to manually using threads). Alternatively, Parallel.Invoke can be used, as shown here.
Just be mindful of the difference between Parallel and Thread and the limitations of Parallel when it comes to performing background tasks, as discussed in this StackOverFlow post.
-------------------------------------------------------------------
EDIT:
Additional remarks and options for clarity:
As pointed out in this StackOverflow post, Parallel.For will not create 1 thread for each item, but use the default ThreadPool instead, which, by default, has number of threads equal to the processor's core count.
One alternative to Parallel.For is using the Task class instead (see below). As discussed in this thread, this also allows the tester to define timeouts for each task (as in Parallel.For though, threads are still taken from the thread pool).
var tasks = new List<Task>();
for (var i = 0; i < count; i++)
{
tasks.Add(Task.Run(() => {
DoSomeWork();
}));
}
Task.WaitAll(tasks.ToArray());
I leave this here as an alternative to the Parallel class that is still much simpler than the original solution.
Upvotes: 1
Reputation: 124044
@ajmastrean, since unit test result must be predictable we need to synchronize threads somehow. I can't see a simple way to do it without using events.
I found that ThreadPool.QueueUserWorkItem
gives me an easy way to test such use cases
ThreadPool.QueueUserWorkItem(x => {
File.Open(fileName, FileMode.Open);
event1.Set(); // Start 2nd tread;
event2.WaitOne(); // Blocking the file;
});
ThreadPool.QueueUserWorkItem(x => {
try
{
event1.WaitOne(); // Waiting until 1st thread open file
File.Delete(fileName); // Simulating conflict
}
catch (IOException e)
{
Debug.Write("File access denied");
}
});
Upvotes: 1
Reputation: 22414
In .NET, ThreadPool
threads won't return without setting up ManualResetEvent
s or AutoResetEvent
s. I find these overkill for a quick test method (not to mention kind of complicated to create, set, and manage). Background worker is a also a bit complex with the callbacks and such.
Something I have found that works is
ThreadStart
method of each thread.public static void MultiThreadedTest()
{
Thread[] threads = new Thread[count];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(DoSomeWork());
}
foreach(Thread thread in threads)
{
thread.Start();
}
foreach(Thread thread in threads)
{
thread.Join();
}
}
Upvotes: 21
Reputation: 38464
Your idea should work fine. Basically you just want to spawn a bunch of threads, and make sure the ones writing the file take long enough to do it to actually make the readers wait. If all of your threads return without error, and without blocking forever, then the test succeeds.
Upvotes: -1