In addition to the thread-safe collections of the. NET concurrent series, sometimes we can have custom implementations, such as using locks, but this causes concurrency to degrade. This article uses the Interlocked class to implement thread-safe queuing.
First define a helper class node, which will act as an element in the queue.
Private class node{ public T Value; Public Node Next; Public Node (T value) { = value; }}
Where we define the node value attribute value and the reference to the next node.
With this node class, we can implement the collection without having to use the. NET Framework's queue class.
This node class is somewhat similar to Haskell's list type, such as [X, [X, [X, [X, [X]]]]. Given a head node and a tail node, the head node is [x, [X, [X, [X, [X]]]], and the next node of the tail node is null, starting from the head node, and with a first-level reference, all the node values can be obtained until the next node of the tail node is met with null.
In order to implement thread-safe queuing, we present the imagined class and the appearance of some fields, as follows
Public class Interlockedqueue<t>{ // Other members private Node head; Private Node tail; Public Interlockedqueue () { new Node (default(T)); = tail = node; }}
Note: We need to use the node class as the private class of the Interlockedqueue class to implement the parameterized type.
With the structure of the storage queue elements, the rest is to consider how to enqueue and dequeue thread-safe operations, using the Interlocked class mentioned earlier is easy to implement.
Enqueue Method implementation
Public voidEnqueue (T value) {node node=NewNode (value); while(true) {Node tail= This. Tail;//Get Current Tail nodeNode next = tail. Next;//Get Current tail ' s next node//must be consistent. If not, the re-enter this while loop if(Object. ReferenceEquals (Tail, This. Tail)) { //next node of tail must be null, otherwise, other node was inserted, and it needs to re-enter the while loop if(Object. ReferenceEquals (Next,NULL) { //begin to insert the This node if(Object. ReferenceEquals (Interlocked.compareexchange (reftail. Next, node, next), next) //(1) {//if consistent, execute insert operation and then the this while loopInterlocked.compareexchanged (ref This. Tail, node, tail); Break; } } Else //Tail was wasn't pointing to the last node//try to swing Tail to the next nodeInterlocked.compareexchange (ref This. Tail, Next, tail); } }}
The main idea is to point the next node of tail to the new node, and then tail to node.
Note: Object at (1). The ReferenceEquals method is primarily used to determine if it has been exchanged, and if there is no exchange, then the if is judged to be false.
Dequeue method implementation
Public BOOLDequeue ( outT value) {Node head; Node tail; Node Next; while(true) { //Read HeadHead = This. Head; Tail= This. Tail; Next=head. Next; //Is head, tail, and next consistent? if(Object.referenceequals ( This. Head, head)) { //is tail falling behind if(Object.referenceequals (head). Next, tail. Next)) {//is the queue empty? if(Object.referenceequals (Next,NULL) ) {value=default(T); //queue is empty and cannot dequeue return false; } Interlocked.compareexchange<Node>( ref This. Tail, next. Next, tail); } Else //No need to deal with tail { //Read value before CAS otherwise another deque might try to free the next nodeValue =Next. Value; //try to swing the head to the next node if(interlocked.compareexchange<node>( ref This. Head, Next, head)==head) { return true; } } } }}
Source Code Reference Network.
Thread-Safe Collections (i)