SoxFan44
SoxFan44

Reputation: 147

Multi-threaded queue consumer and task processing

I'm writing a service that has to read tasks from an AMQP message queue and perform a synchronous action based on the message type. These actions might be to send an email or hit a web service, but will generally be on the order of a couple hundred milliseconds assuming no errors.

I want this to be extensible so that other actions can be added in the future. Either way, the volume of messages could be quite high, with bursts of 100's / second coming in.

I'm playing around with several designs, but my questions are as follows:

  1. What type of threading model should I go with? Do I:

    • a) Go with a single thread to consume from the queue and put tasks on a thread pool? If so, how do I represent those tasks?
    • b) Create multiple threads to host their own consumers and have them handle the task synchronously?
    • c) Create multiple threads to host their own consumers and have them all register a delegate to handle the tasks as they come in?
  2. In the case of a or c, what's the best way to have the spawned thread communicate back with the main thread? I need to ack the message that came off the the queue. Do I raise an event from the spawned thread that the main thread listens to?

  3. Is there a guideline as to how many threads I should run, given x cores? Is it x, 2*x? There are other services running on this system too.

Upvotes: 1

Views: 2029

Answers (1)

Igor Pashchuk
Igor Pashchuk

Reputation: 2491

You should generally* avoid direct thread programming in favor of the Task Parallel Library and concurrent collections built into .NET 4.0 and higher. Fortunately, the producer/consumer problem you described is common and Microsoft has a general-purpose tool for this: the BlockingCollection. This article has a good summary of its features. You may also refer to this white paper for performance analysis of the BlockingCollection<T> (among other things).

However, before pursuing the BlockingCollection<T> or an equivalent, given the scenario you described, why not go for the simple solution of using the Tasks. The TPL gives you the asynchronous execution of tasks with a lot of extras like cancellation and continuation. If, however, you need more advanced lifecycle management, then go for something like a BlockingCollection<T>.


* By "generally", I'm insinuating that the generic solution will not necessarily perform the best for your specific case as it's almost certain that a properly designed custom solution will be better. As with every decision, perform the cost/benefit analysis.

Upvotes: 2

Related Questions