I have some code that when called calls a webservice, queries a database and fetches a value from local cache. It then combines the return values from these three actions to produce its result. Rather than perform these actions sequentially I want to perform them asynchronously in parallel. Here's some dummy/example code :
var waitHandles = new List<WaitHandle>();
var wsResult = 0;
Func<int> callWebService = CallWebService;
var wsAsyncResult = callWebService.BeginInvoke(res => { wsResult = callWebService.EndInvoke(res); }, null);
waitHandles.Add(wsAsyncResult.AsyncWaitHandle);
string dbResult = null;
Func<string> queryDB = QueryDB;
var dbAsyncResult = queryDB.BeginInvoke(res => { dbResult = queryDB.EndInvoke(res); }, null);
waitHandles.Add(dbAsyncResult.AsyncWaitHandle);
var cacheResult = "";
Func<string> queryLocalCache = QueryLocalCache;
var cacheAsyncResult = queryLocalCache.BeginInvoke(res => { cacheResult = queryLocalCache.EndInvoke(res); }, null);
waitHandles.Add(cacheAsyncResult.AsyncWaitHandle);
WaitHandle.WaitAll(waitHandles.ToArray());
Console.WriteLine(string.Format(dbResult, wsResult, cacheResult));
The problem is that the last line throws an error because dbResult is still null when it gets executed. As soon as queryDB.EndInvoke is called the WaitHandle is signalled and execution continues BEFORE the result of queryDB.EndInvoke is assigned to dbResult. Is there a neat/elegant way round this ?
Note : I should add that this affects dbResult simply because the queryDB is the last wait handle to be signalled.
Update : While I accepted Philip's answer which is great, following Andrey's comments, I should add that this also works :
var waitHandles = new List<WaitHandle>();
var wsResult = 0;
Func<int> callWebService = CallWebService;
var wsAsyncResult = callWebService.BeginInvoke(null, null);
waitHandles.Add(wsAsyncResult.AsyncWaitHandle);
string dbResult = null;
Func<string> queryDB = QueryDB;
var dbAsyncResult = queryDB.BeginInvoke(null, null);
waitHandles.Add(dbAsyncResult.AsyncWaitHandle);
var cacheResult = "";
Func<string> queryLocalCache = QueryLocalCache;
var cacheAsyncResult = queryLocalCache.BeginInvoke(null, null);
waitHandles.Add(cacheAsyncResult.AsyncWaitHandle);
WaitHandle.WaitAll(waitHandles.ToArray());
var wsResult = callWebService.EndInvoke(wsAsyncResult);
var dbResult = queryDB.EndInvoke(dbAsyncResult);
var cacheResult = queryLocalCache.EndInvoke(cacheAsyncResult);
Console.WriteLine(string.Format(dbResult, wsResult, cacheResult));
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.
C is an imperative procedural language supporting structured programming, lexical variable scope, and recursion, with a static type system. It was designed to be compiled to provide low-level access to memory and language constructs that map efficiently to machine instructions, all with minimal runtime support.
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.
Unfortunately, the WaitHandle will always be signaled before the EndInvoke()
call returns. Which mean you can't rely on this.
If you cannot use 4.0, a system of threads or manual waithandles is probably going to be in order (or the dreaded Sleep() hack!). You can also have the Invoked method be what sets your results (so EndInvoke happens after the result value is set), but that means moving the results to a shared location, and not local variables - probably requiring a small redesign.
Or If you can use 4.0, I would - System.Threading.Tasks is chock full 'o great stuff. You could rewrite to:
var tasks = new List<Task>();
var wsResult = 0;
string dbResult = null;
var cacheResult = "";
tasks.Add( new Task( ()=> wsResult = CallWebService()));
tasks.Add( new Task( ()=> dbResult = QueryDB()));
tasks.Add( new Task( ()=> cacheResult = QueryLocalCache()));
tasks.ForEach( t=> t.Start());
Task.WaitAll( tasks.ToArray());
Console.WriteLine(string.Format(dbResult, wsResult, cacheResult));
I would go with 3 threads here and avoid Invoke()
. To me, threads are more readable, and you can even put its code into a anonymous method inside the Thread.Start()
.
After starting, you should .Join()
all 3 threads here, and you'll be sure that your results are ready.
It would be something like:
Thread t1=new Thread( delegate() { wsResult = CallWebService(); } );
Thread t2=new Thread( delegate() { dbResult = QueryDb(); } );
Thread t3=new Thread( delegate() { cacheResult = QueryLocalCache(); } );
t1.Start(); t2.Start(); t2.Start();
t1.Join(); t2.Join(); t3.Join();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With