C# 是否有并发无锁阻塞队列的实现?
我知道阻塞队列和无锁队列,这是由提供的实现的一个很好的例子,但是有无锁阻塞队列的实现吗 在无锁阻塞队列中,出列将不需要锁定,但如果队列中没有项目,它将阻塞使用者。有这样一个野兽的实现吗?我更喜欢C#实现,但任何实现在技术上都是可行的 更新: 我想我在第D14.1行遇到了一个竞赛条件:C# 是否有并发无锁阻塞队列的实现?,c#,multithreading,concurrency,lock-free,blockingqueue,C#,Multithreading,Concurrency,Lock Free,Blockingqueue,我知道阻塞队列和无锁队列,这是由提供的实现的一个很好的例子,但是有无锁阻塞队列的实现吗 在无锁阻塞队列中,出列将不需要锁定,但如果队列中没有项目,它将阻塞使用者。有这样一个野兽的实现吗?我更喜欢C#实现,但任何实现在技术上都是可行的 更新: 我想我在第D14.1行遇到了一个竞赛条件: initialize(Q: pointer to queue t) node = new node() // Allocate a free node node–>next.ptr = NULL // Mak
initialize(Q: pointer to queue t)
node = new node() // Allocate a free node
node–>next.ptr = NULL // Make it the only node in the linked list
Q–>Head = Q–>Tail = node // Both Head and Tail point to it
signal = new ManualResetEvent() // create a manual reset event
enqueue(Q: pointer to queue t, value: data type)
E1: node = new node() // Allocate a new node from the free list
E2: node–>value = value // Copy enqueued value into node
E3: node–>next.ptr = NULL // Set next pointer of node to NULL
E4: loop // Keep trying until Enqueue is done
E5: tail = Q–>Tail // Read Tail.ptr and Tail.count together
E6: next = tail.ptr–>next // Read next ptr and count fields together
E7: if tail == Q–>Tail // Are tail and next consistent?
E8: if next.ptr == NULL // Was Tail pointing to the last node?
E9: if CAS(&tail.ptr–>next, next, <node, next.count+1>) // Try to link node at the end of the linked list
E10.1: signal.Set() // Signal to the blocking dequeues
E10.2: break // Enqueue is done. Exit loop
E11: endif
E12: else // Tail was not pointing to the last node
E13: CAS(&Q–>Tail, tail, <next.ptr, tail.count+1>) // Try to swing Tail to the next node
E14: endif
E15: endif
E16: endloop
E17: CAS(&Q–>Tail, tail, <node, tail.count+1>) // Enqueue is done. Try to swing Tail to the inserted node
dequeue(Q: pointer to queue t, pvalue: pointer to data type): boolean
D1: loop // Keep trying until Dequeue is done
D2: head = Q–>Head // Read Head
D3: tail = Q–>Tail // Read Tail
D4: next = head–>next // Read Head.ptr–>next
D5: if head == Q–>Head // Are head, tail, and next consistent?
D6: if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
D7: if next.ptr == NULL // Is queue empty?
D8.1: signal.WaitOne() // Block until an enqueue
D8.X: // remove the return --- return FALSE // Queue is empty, couldn’t dequeue
D9: endif
D10: CAS(&Q–>Tail, tail, <next.ptr, tail.count+1>) // Tail is falling behind. Try to advance it
D11: else // No need to deal with Tail
// Read value before CAS, otherwise another dequeue might free the next node
D12: *pvalue = next.ptr–>value
D13: if CAS(&Q–>Head, head, <next.ptr, head.count+1>) // Try to swing Head to the next node
D14.1: if(head.ptr == tail.ptr && next.ptr==NULL) // Is queue empty? <--- POSSIBLE RACE CONDITION???
D14.2: signal.Reset()
D14.3: break // Dequeue is done. Exit loop
D15: endif
D16: endif
D17: endif
D18: endloop
D19: free(head.ptr) // It is safe now to free the old dummy node
D20: return TRUE // Queue was not empty, dequeue succeeded
初始化(Q:指向队列t的指针)
node=new node()//分配一个空闲节点
node–>next.ptr=NULL//使其成为链接列表中的唯一节点
Q–>头部=Q–>尾部=节点//头部和尾部都指向它
信号=新建ManualResetEvent()//创建手动重置事件
排队(Q:指向队列t的指针,值:数据类型)
E1:node=new node()//从空闲列表中分配一个新节点
E2:node–>value=value//将排队的值复制到节点中
E3:node–>next.ptr=NULL//将节点的下一个指针设置为NULL
E4:循环//继续尝试,直到排队完成
E5:tail=Q–>tail//一起读取tail.ptr和tail.count
E6:next=tail.ptr–>next//一起读取next ptr和count字段
E7:如果tail==Q–>tail//tail和next是否一致?
E8:如果next.ptr==NULL//尾部是否指向最后一个节点?
E9:如果CAS(&tail.ptr–>下一步,下一步)//尝试链接链接列表末尾的节点
E10.1:signal.Set()//向阻塞队列发送信号
E10.2:中断//排队完成。出口回路
E11:endif
E12:else//Tail未指向最后一个节点
E13:CAS(&Q–>Tail,Tail,)//尝试将Tail摆动到下一个节点
E14:endif
E15:endif
E16:端环
E17:CAS(&Q–>Tail,Tail,)//排队完成。尝试将尾部摆动到插入的节点
出列(Q:指向队列t的指针,pvalue:指向数据类型的指针):布尔值
D1:循环//继续尝试,直到完成出列
D2:head=Q–>head//Read head
D3:tail=Q–>tail//读取tail
D4:next=head–>next//Read head.ptr–>next
D5:如果head==Q–>head//,那么head、tail和next是否一致?
D6:如果head.ptr==tail.ptr//队列是空的还是tail落后?
D7:如果next.ptr==NULL//队列是否为空?
D8.1:signal.WaitOne()//阻塞直到排队
D8.X://删除返回---return FALSE//队列为空,无法退出队列
D9:endif
D10:CAS(&Q–>Tail,Tail,)//Tail落后了。努力推进它
D11:否则//不需要处理尾部
//在CAS之前读取值,否则另一个出列可能会释放下一个节点
D12:*pvalue=next.ptr–>value
D13:如果CAS(&Q–>Head,Head,)//尝试将Head摆动到下一个节点
D14.1:if(head.ptr==tail.ptr&&next.ptr==NULL)//队列是否为空 .NET并行扩展:(内置,适用于.NET 4.0+):
StackOverflow的实现中有人:
对评论中澄清的答复:
如果阻塞when empty不忙(等待信号),那么您似乎需要一个计数信号量来等待
另一种方法是使用常规队列以及原子比较和交换或自旋锁来防止同时访问,
然后,如果消费者线程试图在队列为空时进入,请锁定二进制信号量,
若提供者线程试图在队列为空时进入,则解锁二进制信号量以唤醒所有睡眠者消费者(并将其返回到自旋锁,以便多个线程只能在队列中有足够的项目时才能进入)
例如://伪代码
/// Non-blocking lock (busy wait)
void SpinLock()
{
While (CompareAndExchange(myIntegerLock, -1, 0) != 0)
{
// wait
}
}
void UnSpinLock()
{
Exchange(myIntegerLock, 0);
}
void AddItem(item)
{
// Use CAS for synchronization
SpinLock(); // Non-blocking lock (busy wait)
queue.Push(item);
// Unblock any blocked consumers
if (queue.Count() == 1)
{
semaphore.Increase();
}
// End of CAS synchronization block
UnSpinLock();
}
Item RemoveItem()
{
// Use CAS for synchronization
SpinLock(); // Non-blocking lock (busy wait)
// If empty then block
if (queue.Count() == 0)
{
// End of CAS synchronization block
UnSpinLock();
// Block until queue is not empty
semaphore.Decrease();
// Try again (may fail again if there is more than one consumer)
return RemoveItem();
}
result = queue.Pop();
// End of CAS synchronization block
UnSpinLock();
return result;
}
编辑:
更简单:
我建议你排队时不要头尾颠倒。只要有头脑。如果head=NULL,则列表为空。将项目添加到标题。从头部移除物品。更简单、更少的CAS操作
助手:
我在评论中建议,你需要考虑一个助手计划来处理比赛。在我的版本中,“无锁”的意思是,如果很少的比赛条件没有引起问题的话,也可以。我喜欢与空闲线程睡眠时间过长几毫秒相比的额外性能
助手的想法。当消费者抓取工作时,它可以检查昏迷中是否有线程。当制作人添加工作时,它可以在comas中查找线程
所以,轨道枕木。使用轨枕的链接列表。当线程决定没有工作时,它会将自己标记为!醒来后,CAS将自己排在睡眠者名单的首位。当接收到唤醒信号时,线程会将self标记为唤醒。然后,新唤醒的线程清理睡眠者列表。要清理并发的单链表,您必须小心。你只能打到头。因此,当睡眠者列表的头被标记为唤醒时,你可以将头去掉。如果头部未唤醒,则继续扫描列表并“懒散地取消链接”(我编造了该术语)剩余的唤醒项。延迟取消链接很简单…只需在唤醒项上设置上一项的下一个ptr。即使同时扫描到的项目是正确的,它仍然会到达列表的末尾!唤醒后续扫描会看到一个较短的列表。最后,任何时候你增加工作或完成工作,扫描睡眠者名单!唤醒项目。如果使用者在抓取某些工作(.next work!=NULL)后注意到仍有工作,则使用者可以扫描睡眠者列表,并向第一个正在执行的线程发送信号!唤醒制片人添加作品后,可以扫描睡眠者列表并执行相同操作
如果您有一个广播场景,并且无法向单个线程发送信号,那么只需记录休眠线程的数量。当该计数仍然大于0时,一个consu