我正在阅读的其中一本书是“莫里斯·赫利希和尼尔·沙维特的多处理器编程艺术”。在其中,有一个“无等待”队列(在一些语言自适应之后)在线程环境中完美地运行测试和逻辑 - 至少,即使在分布在5个线程上的10,000,000个项目上也没有冲突逻辑检查出来。
(如果无法获取某个项目,我编辑了队列以返回false,而不是抛出异常。代码如下所示。)
然而,它有一个警告;队列无法增长。一个粗略的逻辑检查表明它不会在没有锁定队列的情况下增长 - 这在某种程度上否定了拥有无锁队列的重要性。
然后,目的是创建一个可以增长的无锁(或至少是无饥饿锁定)队列。
所以:如果我们基本上给每个线程都有自己的共享队列,那么这种方式不是矛盾的(并且接受这个已被解决的概率很高,并且更好地用于边做边学):
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;
}
}
所以:这会有用吗?如果没有,为什么?如果是这样,还有任何可预见的问题吗?
感谢。
答案 0 :(得分:5)
尝试编写无锁多线程代码很难,你应该把它留给比你或我更了解它的人(并使用例如ConcurrentQueue<T>
),或者根本不写(并且如果可能的话使用锁。
话虽如此,您的代码存在一些问题:
threadCount
为2,并且您将项目1,2和3一个接一个地排队,然后您出列,那么您将获得第2项!您不能先使用字段的值,然后像上面那样调用Interlocked.Increment()
。想象一下这样的事情:
items[tail % items.Length] = value;
items[tail % items.Length] = value;
Interlocked.Increment(ref head);
Interlocked.Increment(ref head);
现在,两个线程排队到同一个位置,之后的位置没有变化。这是错误的。
答案 1 :(得分:2)
您发布的代码对于入队或出队都不是线程安全的。
<强>入队强>
head = tail = 0
Enqueue
并在items[tail % items.Length] = value;
之后但在执行互锁增量之前被中断。它现在已将值写入item[0]
但未递增tail
Enqueue
并执行该方法。现在tail
仍为0,因此前一个线程写入的值将被替换,因为第二个线程也会将值写入item[0]
。 <强>出列强>
item[0]
和items[1]
head = 0
和tail = 2
Dequeue
并在r = items[head % items.Length];
之后但在执行互锁增量之前被中断。Dequeue
并返回与head
相同的项目仍为0