/// <summary> /// Starts a multi-threaded queue listener that uses the specified number of dequeue threads. /// </summary> /// <param name="threadCount">The number of dequeue threads.</param> public void StartListener(int threadCount) { Guard.ArgumentNotZeroOrNegativeValue(threadCount, "threadCount"); var callToken = TraceManager.WorkerRoleComponent.TraceIn(this.queueLocation.StorageAccount, this.queueLocation.QueueName, threadCount); try { // The collection of dequeue tasks needs to be reset on each call to this method. if (this.dequeueTasks.IsAddingCompleted) { this.dequeueTasks = new BlockingCollection <Task>(this.dequeueTaskList); } for (int i = 0; i < threadCount; i++) { CancellationToken cancellationToken = this.cancellationSignal.Token; CloudQueueListenerDequeueTaskState <T> workerState = new CloudQueueListenerDequeueTaskState <T>(Subscriptions, cancellationToken, this.queueLocation, this.queueStorage); // Start a new dequeue task and register it in the collection of tasks internally managed by this component. this.dequeueTasks.Add(Task.Factory.StartNew(DequeueTaskMain, workerState, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); } // Mark this collection as not accepting any more additions. this.dequeueTasks.CompleteAdding(); } finally { TraceManager.WorkerRoleComponent.TraceOut(callToken); } }
/// <summary> /// Implements a task performing dequeue operations against a given Windows Azure queue. /// </summary> /// <param name="state">An object containing data to be used by the task.</param> private void DequeueTaskMain(object state) { CloudQueueListenerDequeueTaskState <T> workerState = (CloudQueueListenerDequeueTaskState <T>)state; var callToken = TraceManager.WorkerRoleComponent.TraceIn(workerState.QueueLocation.StorageAccount, workerState.QueueLocation.QueueName); int idleStateCount = 0; TimeSpan sleepInterval = DequeueInterval; try { // Run a dequeue task until asked to terminate or until a break condition is encountered. while (workerState.CanRun) { try { var queueMessages = from msg in workerState.QueueStorage.Get <T>(workerState.QueueLocation.QueueName, DequeueBatchSize, workerState.QueueLocation.VisibilityTimeout).AsParallel() where msg != null select msg; int messageCount = 0; // Check whether or not work items arrived to a queue while the listener was idle. if (idleStateCount > 0 && queueMessages.Count() > 0) { if (QueueWorkDetected != null) { QueueWorkDetected(this); } } // Process the dequeued messages concurrently by taking advantage of the above PLINQ query. queueMessages.ForAll((message) => { // Reset the count of idle iterations. idleStateCount = 0; // Notify all subscribers that a new message requires processing. workerState.OnNext(message); // Once successful, remove the processed message from the queue. workerState.QueueStorage.Delete <T>(message); // Increment the number of processed messages. messageCount++; }); // Check whether or not we have done any work during this iteration. if (0 == messageCount) { // Increment the number of iterations when we were not doing any work (e.g. no messages were dequeued). idleStateCount++; // Call the user-defined delegate informing that no more work is available. if (QueueEmpty != null) { // Check if the user-defined delegate has requested a halt to any further work processing. if (QueueEmpty(this, idleStateCount, out sleepInterval)) { TraceManager.WorkerRoleComponent.TraceInfo(String.Format(CultureInfo.CurrentCulture, TraceLogMessages.CloudQueueListenerTerminatedWithNoMoreWork, this.queueLocation.QueueName, this.queueLocation.StorageAccount)); // Terminate the dequeue loop if user-defined delegate advised us to do so. break; } } // Enter the idle state for the defined interval. Thread.Sleep(sleepInterval); } } catch (Exception ex) { if (ex is OperationCanceledException) { throw; } else { // Offload the responsibility for handling or reporting the error to the external object. workerState.OnError(ex); // Sleep for the specified interval to avoid a flood of errors. Thread.Sleep(sleepInterval); } } } } finally { workerState.OnCompleted(); TraceManager.WorkerRoleComponent.TraceOut(callToken, workerState.QueueLocation.StorageAccount, workerState.QueueLocation.QueueName); } }