gdp
gdp

Reputation: 8242

Return to calling method - BlockingCollection queue items

i need some help with ConcurrentQueue's and the BlockingCollection.

The scenario is im trying to throttle requests and conform to a 1 request per second limit, the throttling will occur when i dequeue an item from the queue. The application is a MVC 4 app, so there potentially can be multiple producers at any given time, and there is only one consumer/web service i am contacting.

In short i would like to process an item in the queue, download the response and send it back to the calling method. Sending it back to the calling method is where im stuck. What options do i have here?

//I want to do something like this, and wait for the throttled response to return
public class WebService()
{
    public string GetUser(string name) 
    {
         var url = buildUrl(name);

         var response = string.Empty;

         var downloadTask = Task.Factory.StartNew( () => {
               response = WebServiceHelper.ThrottledDownload(url);
         });
         downloadTask.Wait();
         return response;
    }
}

public static class WebServiceHelper()
{
  private static BlockingCollection<Request> requests = new BlockingCollection<Request>();

  static WebServiceHelper()
  {
       foreach(var item in requests.GetEnumerableConsumer()) {
         string response = DoWork(item.Url);
         //How can i send this back to the calling method?
       }
  }

  public static string ThrottledDownload(string url)
  {
     //Add the request to the blocking queue 
     requests.Add(new Request(url, someId));

     //How do i get the result of the DoWork method?
  } 
}

Upvotes: 2

Views: 1283

Answers (1)

Jim Mischel
Jim Mischel

Reputation: 134005

You probably don't want to "return a result" to the ThrottledDown method. At least, I wouldn't think so. If you did want to do that, you'd have to make some kind of blocking call. Or use a Task with continuation ... perhaps async in C# 5.

It's still unclear what the main thread is doing. I assume it's queuing a bunch of requests that the consumer thread processes at one-second intervals (presumably to prevent you from being throttled by the server that you're querying). You then want the main thread (or something) to be notified so that it can ... do something.

The flow of your program is still unclear.

Depending on what you want the main thread to do (and what information you want it to do that with), you have a number of options. You can:

  • Create another BlockingCollection of results. When the consumer completes a request, it adds an object to that collection. The main thread polls that collection to get notification of request completions.
  • A variation on the above is to use a pipeline. One thread queues requests. One thread dequeues requests, makes Web requests, and then puts results into another queue. A third thread processes that second queue.
  • Add an event (i.e. ManualResetEventSlim, for example) to each of your Request objects. The consumer calls Set on that event when it's completed the request. The main thread either waits on that event, or polls it periodically.
  • Have the consumer execute a callback function (defined at compile time, or passed in the Request object that you add to the queue). That callback function can notify the main thread, log the result, or whatever you like.

Again, without more information about your application and the higher level problem you're trying to solve, it's rather difficult to make more specific recommendations.

Upvotes: 1

Related Questions