views:

279

answers:

3

What would be the best way to do multithreading or asynchronous task in the following situation in C#?

The simplified situation:

A http request needs to make 5 or more web service calls. Upon completion each web service call will receive and return a string list as a result. The caller (of 5 web service calls) need to merge the 5 results into a single string list and return it to the http caller.

Because each thread needs to return a value in the end so I am wondering if Asynchronous Delegates is the way to go. Because I am not so experienced in this area so I am asking this questions and/or suggestions.

Thank you!

+3  A: 

You should have a look at QueueUserWorkItem. This would allow you to do each call on a separate thread and get the string value based on the particular call e.g.

ManualResetEvent[] calls = new ManualResetEvent[5];
string[] results = new string[5];

calls[0] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(t => 
{
    results[0] = // do webservice call
    calls[0].Set();
});

calls[1] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(t => 
{
    results[1] = // do webservice call
    calls[1].Set();
});

....
// wait for all calls to complete
WaitHandle.WaitAll(calls);
// merge the results into a comma delimited string
string resultStr = String.Join(", ", results);
James
Thanks James. Can you please explain a bit more on the pros and cons comparing to using Asynchronous Delegates. Or did I misunderstood the use of Asynchronous Delegates? Thanks.
Jeffrey C
You would need to create a counter to keep track of the delegates you started, and wait for them to finish. Much in the same way the above code works. The .Set() line in the above code would work as EndInvoke, but keeps the program flow cleaner imo.
Mikael Svenson
@Jeffrey, you are essentially using Anonymous delegates in the manner I have shown above. Difference is the QueueUserWorkItem will run the delegate on a separate thread which allows you to make multiple calls asynchronously (like you required).
James
A: 

You need to use a callback to accomplish what you're asking.

The easiest way to do this if you're not using the 4.0 framework is as follows:

var strings = new List<string>();
for (var i = 0; i < 5; i++)
{
    ThreadPool.QueueUserWorkItem((x) => strings.AddRange(SomeWebServiceCall(i)));
}

This requires some synchronization after the loop to ensure the threads complete before you move on. In the 4.0 framework, the Task Parallel Library does that work for you:

Parallel.For(0, 5, i => strings.AddRange(someWebServiceCall(i)));

Asynchronous delegates were useful when the 1.0 framework came out, but are a lot of extra framework to deal with since the addition of anonymous delegates in 2.0. In 4.0, the TPL makes them even more irrelevant. IAsyncResult relies on an explicit callback, and you can now use implicit callbacks (as demonstrated above).

Michael Meadows
It's not really less lines of code. I omitted the synchronization code, whereas @James included it. As with most things, there are at least 10 ways to skin a cat.
Michael Meadows
@Michael, Thanks! Also it's actually for work.
Jeffrey C
@Jeffrey, I removed that edit. You were pushing pretty hard for IAsyncResult, so I thought it was a requirement. Usually when a specific approach (especially an outdated one) is required, it's because some professor wants to teach you something. Enjoy.
Michael Meadows
@Michael, thanks. When it comes to multithreading in C# I am pretty out dated. I've been doing a lot of business apps that required more hardware to scale rather than doing it smart. So...
Jeffrey C
Sometimes scaling hardware up (beefing up a single machine) is more cost effective than programming for parallelism. The problem becomes when you have to scale out (more servers). Unfortunately, if you don't design it right from the beginning, you may be incurring technical debt that you have to pay off when you redesign for distribution.
Michael Meadows
+1  A: 

Here's a short piece og code using .Net 4.0 which utilizes the new System.Collections.Concurrent which encapsulates the concurrency code:

class StackOverflowParalell
{
    public void Execute()
    {
        List<int> codeParam = new List<int>(){1,2,3};
        ConcurrentBag<string> result = new ConcurrentBag<string>();
        Parallel.For(0, codeParam.Count, i => DoSometing(i).ForEach( result.Add ));
        // return result here as List, Array....
    }

    List<string> DoSometing(int value)
    {
        return new List<string>(){"1","2","3","4"};
    }
}
Mikael Svenson