Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting on multiple asynchronous POST requests

I need to make multiple asynchronous calls from inside a wcf service hosted in IIS (might be relevant, I don't know). The calls are to other services, but I make them by POSTing a string to the service URL. Calling them synchronously works, but I have more than a dozen calls, and they're completely independent, so I would like to speed them up by calling them asynchronously.

Now, I realize that I could simply use multiple threads for this - and it might be the easiest solution - but I thought I'd give async a try.

My code is similar to this:

public delegate void Callback(string response);

public static void InvokeAsync(string request, string url, Callback callback)
  where TResponse: class
{
  var web = new WebClient();
  web.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

  web.UploadStringCompleted += (sender, e) => callback.Invoke(e.Result);
  web.UploadStringAsync(new Uri(url), request);
}

//...
var lockObj = new object();
foreach (var item in list)
  InvokeAsync(item, url, response => { lock(lockObj) newList.Add(response); });

The above should work, I believe; however, my problem is that I have no idea how to wait until all the responses are back. If I use a counter and wait in a loop until it becomes zero, there won't be any CPU left to handle the callbacks. Same with using some form of semaphore, I believe. Is there a non-blocking way to wait until I got back all the responses? (Changing the method signature to also take a callback will only move the problem one level up, I'm afraid.)

[Edit] Here's a very close question - How to invoke async operation as sync? - but I need to wait on multiple async operations.

like image 713
Marcel Popescu Avatar asked Nov 12 '10 15:11

Marcel Popescu


1 Answers

You use an AutoResetEvent for this.

Try the following for your last two lines:

var waitEvent = new AutoResetEvent(false);

foreach (var item in list)
  InvokeAsync(item, url, response => { lock(lockObj) newList.Add(response); waitEvent.Set(); });

while (newList.Count < list.Count)
  waitEvent.WaitOne();

All items should be completed when newList contains as many items as list. When a new item comes in, you add it to newList. Then, you signal the waitEvent that "something" happened to newList.

In the main thread, you simply wait until you have enough items in newList and you wait for this by waiting on changes to newList, which waitEvent informs you of.

like image 162
Pieter van Ginkel Avatar answered Oct 20 '22 06:10

Pieter van Ginkel