/// <summary> /// Dequeue a WorkItem for processing /// </summary> /// <returns>A WorkItem or null if the queue has stopped</returns> public WorkItem Dequeue() { SpinWait sw = new SpinWait(); do { WorkItemQueueState cachedState = State; if (cachedState == WorkItemQueueState.Stopped) { return(null); // Tell worker to terminate } int cachedRemoveId = _removeId; int cachedAddId = _addId; // Empty case (or paused) if (cachedRemoveId == cachedAddId || cachedState == WorkItemQueueState.Paused) { // Spin a few times to see if something changes if (sw.Count <= spinCount) { sw.SpinOnce(); } else { // Reset to wait for an enqueue _mreAdd.Reset(); // Recheck for an enqueue to avoid a Wait if ((cachedRemoveId != _removeId || cachedAddId != _addId) && cachedState != WorkItemQueueState.Paused) { // Queue is not empty, set the event _mreAdd.Set(); continue; } // Wait for something to happen _mreAdd.Wait(500); } continue; } // Validate that we are the current dequeuer if (Interlocked.CompareExchange(ref _removeId, cachedRemoveId + 1, cachedRemoveId) != cachedRemoveId) { continue; } // Dequeue our work item WorkItem work; while (!_innerQueue.TryDequeue(out work)) { } ; // Add to items processed using CAS Interlocked.Increment(ref _itemsProcessed); return(work); } while (true); }
/// <summary> /// Removes the first element from the queue and returns it (or <c>null</c>). /// </summary> /// <param name="blockWhenEmpty"> /// If <c>true</c> and the queue is empty, the calling thread is blocked until /// either an element is enqueued, or <see cref="Stop"/> is called. /// </param> /// <returns> /// <list type="bullet"> /// <item> /// <term>If the queue not empty</term> /// <description>the first element.</description> /// </item> /// <item> /// <term>otherwise, if <paramref name="blockWhenEmpty"/>==<c>false</c> /// or <see cref="Stop"/> has been called</term> /// <description><c>null</c>.</description> /// </item> /// </list> /// </returns> public Event Dequeue(bool blockWhenEmpty) { SpinWait sw = new SpinWait(); do { int cachedRemoveId = _removeId; int cachedAddId = _addId; // Empty case if (cachedRemoveId == cachedAddId) { if (!blockWhenEmpty || _stopped != 0) { return(null); } // Spin a few times to see if something changes if (sw.Count <= spinCount) { sw.SpinOnce(); } else { // Reset to wait for an enqueue _mreAdd.Reset(); // Recheck for an enqueue to avoid a Wait if (cachedRemoveId != _removeId || cachedAddId != _addId) { // Queue is not empty, set the event _mreAdd.Set(); continue; } // Wait for something to happen _mreAdd.Wait(500); } continue; } // Validate that we are the current dequeuer if (Interlocked.CompareExchange(ref _removeId, cachedRemoveId + 1, cachedRemoveId) != cachedRemoveId) { continue; } // Dequeue our work item Event e; while (!_queue.TryDequeue(out e)) { if (!blockWhenEmpty || _stopped != 0) { return(null); } } return(e); } while (true); }