Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这个逻辑/代码链是否有明显的问题?_C#_Multithreading_Queue - Fatal编程技术网

C# 这个逻辑/代码链是否有明显的问题?

C# 这个逻辑/代码链是否有明显的问题?,c#,multithreading,queue,C#,Multithreading,Queue,我正在读的一本书是《多处理机编程的艺术》,作者是莫里斯·赫利希和尼尔·沙维特。在其中,有一个“无等待”队列(经过一点语言调整后),它在线程环境中完美地执行测试和逻辑功能——至少,即使在分布在5个线程中的10000000个项目上也没有冲突,并且逻辑已签出 (如果无法获取项目,我编辑队列以返回false,而不是抛出异常。下面给出了代码) 然而,它有一个警告;队列无法增长。粗略的逻辑检查表明,如果不锁定队列,它就无法增长,这在某种程度上否定了无锁队列的重要性 然后,目的是创建一个可以增长的无锁(或至少

我正在读的一本书是《多处理机编程的艺术》,作者是莫里斯·赫利希和尼尔·沙维特。在其中,有一个“无等待”队列(经过一点语言调整后),它在线程环境中完美地执行测试和逻辑功能——至少,即使在分布在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()
    。想象一下这样的情景:

  • 在线程1上:
    items[tail%items.Length]=值
  • 在线程2上:
    items[tail%items.Length]=值
  • 在螺纹2上:
    联锁。增量(参考头)
  • 在螺纹1上:
    联锁。增量(参考头)
  • 现在,两个线程排队到同一位置,之后的位置没有改变。这是错误的


    您发布的代码对于入队和出队都不是线程安全的

    排队

    • 你有一个空队列
    • head=tail=0
    • 您有2个线程试图排队
    • 第一个线程执行
      Enqueue
      ,并在
      items[tail%items.Length]=value之后被中断但在执行联锁增量之前。它现在已将一个值写入
      项[0]
      ,但未递增
      tail
    • 第二个线程进入队列并执行该方法。现在
      tail
      仍然是0,因此前一个线程写入的值被替换,因为第二个线程也会将值写入
      项[0]
    • 两个线程都完成了执行
    • 项目计数将是正确的,但您将丢失数据
    出列

    • 假设队列中有两个项目,分别位于
      项目[0]
      项目[1]
    • head=0
      tail=2
    • 您有两个线程试图退出队列
    • 第一个线程进入
      Dequeue
      并在
      r=items[head%items.Length]之后中断但在执行联锁增量之前
    • 现在,第二个线程进入
      Dequeue
      并返回与
      head
      仍然为0相同的项目
    • 之后,您的队列将为空,但您将读取一个项目两次,一个项目根本没有读取

    为什么不直接使用
    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;
        }
    }