C# 这个逻辑/代码链是否有明显的问题?
我正在读的一本书是《多处理机编程的艺术》,作者是莫里斯·赫利希和尼尔·沙维特。在其中,有一个“无等待”队列(经过一点语言调整后),它在线程环境中完美地执行测试和逻辑功能——至少,即使在分布在5个线程中的10000000个项目上也没有冲突,并且逻辑已签出 (如果无法获取项目,我编辑队列以返回false,而不是抛出异常。下面给出了代码) 然而,它有一个警告;队列无法增长。粗略的逻辑检查表明,如果不锁定队列,它就无法增长,这在某种程度上否定了无锁队列的重要性 然后,目的是创建一个可以增长的无锁(或至少是无饥饿锁定)队列 因此:如果从本质上讲,我们以一种非矛盾的方式给每个线程分配它们自己的共享队列(并且接受这种可能性很高,即这个问题已经得到解决,并且更好地实现边做边学的目的):C# 这个逻辑/代码链是否有明显的问题?,c#,multithreading,queue,C#,Multithreading,Queue,我正在读的一本书是《多处理机编程的艺术》,作者是莫里斯·赫利希和尼尔·沙维特。在其中,有一个“无等待”队列(经过一点语言调整后),它在线程环境中完美地执行测试和逻辑功能——至少,即使在分布在5个线程中的10000000个项目上也没有冲突,并且逻辑已签出 (如果无法获取项目,我编辑队列以返回false,而不是抛出异常。下面给出了代码) 然而,它有一个警告;队列无法增长。粗略的逻辑检查表明,如果不锁定队列,它就无法增长,这在某种程度上否定了无锁队列的重要性 然后,目的是创建一个可以增长的无锁(或至少
WaitFreeQueue队列=新的WaitFreeQueue(threadCount);
//让队列出队,让项目入队,让队列入队。
//让队列出队,让项目出队,让队列入队。
以及无等待队列(前面的代码包含在注释中,以防我做出任何破坏性的更改):
//
///线程环境中使用的无等待队列。
///显著改编自“Maurice Herlihy和Nir Shavit的多处理器编程艺术”。
///
///队列中项目的类型。
公共类等待队列
{
///
///要从中退出队列的索引。
///
保护头部;
///
///要排队的索引。
///
保护尾;
///
///要排队的数组。
///
受保护的物品;
///
///排队的项目数。
///
公共整数计数
{
获取{return tail-head;}
}
///
///创建一个新的无等待队列。
///
///队列的容量。
公共队列(整数容量)
{
项目=新T[容量];
头部=0;尾部=0;
}
///
///尝试使项目排队。
///
///要排队的项目。
///如果队列中没有空间,则返回false。
公共布尔排队(T值)
{
if(tail-head==items.Length)
//抛出新的IndexOutOfRangeException();
返回false;
项目[尾%items.Length]=值;
系统。螺纹。联锁。增量(参考尾);
返回true;
//tail++;
}
///
///尝试将项目出列。
///
///要退出队列的变量。
///如果存在要出列的可用项,则返回true。
公共布尔出列(out T r)
{
如果(尾部-头部==0)
//抛出新的InvalidOperationException(“无更多项目”);
{r=default(T);返回false;}
r=项目[总项目数%。项目长度];
系统。螺纹。联锁。增量(参考头);
//head++;
//返回r;
返回true;
}
}
那么:这样行吗?若否,原因为何?如果是的话,还有什么可预见的问题吗
谢谢。尝试编写无锁多线程代码很难,您应该将它留给比您或我更了解它的人(并使用例如
ConcurrentQueue
),或者如果可能,根本不编写它(并使用锁)
也就是说,您的代码有几个问题:
threadCount
为2,您依次将项目1、2和3排队,然后您退出队列,则您将获得项目2李>
Interlocked.Increment()
。想象一下这样的情景:
items[tail%items.Length]=值代码>
items[tail%items.Length]=值代码>
联锁。增量(参考头)代码>
联锁。增量(参考头)代码>
您发布的代码对于入队和出队都不是线程安全的 排队
- 你有一个空队列
head=tail=0
- 您有2个线程试图排队
- 第一个线程执行
,并在Enqueue
items[tail%items.Length]=value之后被中断代码>但在执行联锁增量之前。它现在已将一个值写入
,但未递增项[0]
tail
- 第二个线程进入队列并执行该方法。现在
仍然是0,因此前一个线程写入的值被替换,因为第二个线程也会将值写入tail
李>项[0]
- 两个线程都完成了执行李>
- 项目计数将是正确的,但您将丢失数据
- 假设队列中有两个项目,分别位于
和项目[0]
项目[1]
和head=0
tail=2
- 您有两个线程试图退出队列
- 第一个线程进入
并在Dequeue
r=items[head%items.Length]之后中断代码>但在执行联锁增量之前
- 现在,第二个线程进入
并返回与Dequeue
仍然为0相同的项目head
- 之后,您的队列将为空,但您将读取一个项目两次,一个项目根本没有读取
ConcurrentQueue
?或者这是一个学习练习?如果两个线程同时调用排队
,并且控件从
WaitFreeQueue<Queue<int>> queues = new WaitFreeQueue<Queue<int>>(threadCount);
// Dequeue a queue, enqueue an item, enqueue the queue.
// Dequeue a queue, dequeue an item, enqueue the queue.
/// <summary>
/// A wait-free queue for use in threaded environments.
/// Significantly adapted from "The Art of Multiprocessor Programming by Maurice Herlihy and Nir Shavit".
/// </summary>
/// <typeparam name="T">The type of item in the queue.</typeparam>
public class WaitFreeQueue<T>
{
/// <summary>
/// The index to dequeue from.
/// </summary>
protected int head;
/// <summary>
/// The index to queue to.
/// </summary>
protected int tail;
/// <summary>
/// The array to queue in.
/// </summary>
protected T[] items;
/// <summary>
/// The number of items queued.
/// </summary>
public int Count
{
get { return tail - head; }
}
/// <summary>
/// Creates a new wait-free queue.
/// </summary>
/// <param name="capacity">The capacity of the queue.</param>
public WaitFreeQueue(int capacity)
{
items = new T[capacity];
head = 0; tail = 0;
}
/// <summary>
/// Attempts to enqueue an item.
/// </summary>
/// <param name="value">The item to enqueue.</param>
/// <returns>Returns false if there was no room in the queue.</returns>
public bool Enqueue(T value)
{
if (tail - head == items.Length)
// throw new IndexOutOfRangeException();
return false;
items[tail % items.Length] = value;
System.Threading.Interlocked.Increment(ref tail);
return true;
// tail++;
}
/// <summary>
/// Attempts to dequeue an item.
/// </summary>
/// <param name="r">The variable to dequeue to.</param>
/// <returns>Returns true if there was an available item to dequeue.</returns>
public bool Dequeue(out T r)
{
if (tail - head == 0)
// throw new InvalidOperationException("No more items.");
{ r = default(T); return false; }
r = items[head % items.Length];
System.Threading.Interlocked.Increment(ref head);
// head++;
// return r;
return true;
}
}