【问题标题】:What is the best alternative for a concurrent task queue, not using .net 4.0并发任务队列的最佳替代方案是什么,而不是使用 .net 4.0
【发布时间】:2014-02-20 12:43:25
【问题描述】:

我正在尝试实现一个框架,以便在 Unity 应用程序中使用多个内核。

到目前为止,我所拥有的是由 Julian M Bucknal 实现的non-blocking queue。它似乎对我的目的非常有效。我创建了一个任务类和一个结果类,并使用两个队列来分发和收集在辅助线程上处理的工作。

剩下的问题是,由于这段代码将在我的应用程序的性能关键部分中大量使用,我真的希望尽量减少垃圾收集器的使用。

是否有已知的库/模板/技术可以帮助我进行这项改进?

using System.Threading;

public class LockFreeQueue<T> {
    internal struct SingleLinkNode<U> where U : T {
        // Note; the Next member cannot be a property since
        // it participates in many CAS operations
        public SingleLinkNode<U> Next;
        public U Item;
    }
    static private bool CAS<V>(ref V location, V comparand, V newValue) where V : class {
        return
            (object) comparand ==
            (object) Interlocked.CompareExchange<V>(ref location, newValue, comparand);
    }

    SingleLinkNode<T> head;
    SingleLinkNode<T> tail;

    public LockFreeQueue() {
        head = new SingleLinkNode<T>();
        tail = head;
    }

    public void Enqueue(T item) {
        SingleLinkNode<T> oldTail = null;
        SingleLinkNode<T> oldTailNext;

        SingleLinkNode<T> newNode = new SingleLinkNode<T>();
        newNode.Item = item;

        bool newNodeWasAdded = false;
        while (!newNodeWasAdded) {
            oldTail = tail;
            oldTailNext = oldTail.Next;

            if (tail == oldTail) {
                if (oldTailNext == null)
                    newNodeWasAdded = CAS<SingleLinkNode<T>>(ref tail.Next, null, newNode);
                else
                    CAS<SingleLinkNode<T>>(ref tail, oldTail, oldTailNext);
            }
        }
        CAS<SingleLinkNode<T>>(ref tail, oldTail, newNode);
    }

    public bool Dequeue(out T item) {
        item = default(T);
        SingleLinkNode<T> oldHead = null;

        bool haveAdvancedHead = false;
        while (!haveAdvancedHead) {

            oldHead = head;
            SingleLinkNode<T> oldTail = tail;
            SingleLinkNode<T> oldHeadNext = oldHead.Next;

            if (oldHead == head) {
                if (oldHead == oldTail) {
                    if (oldHeadNext == null) {
                        return false;
                    }
                    CAS<SingleLinkNode<T>>(ref tail, oldTail, oldHeadNext);
                } else {
                    item = oldHeadNext.Item;
                    haveAdvancedHead = CAS<SingleLinkNode<T>>(ref head, oldHead, oldHeadNext);
                }
            }
        }
        return true;
    }

    public T Dequeue() {
        T result;
        Dequeue(out result);
        return result;
    }
}

【问题讨论】:

  • 嗯,如果您正在寻找迁移到 .NET 4 的充分理由,那么您找到了。这个问题问得太晚了五年。
  • 我猜 Unity 有十几个理由不这样做……但我希望他们能这样做。
  • 嗯...如果我有一个非阻塞堆栈,它不会产生太多垃圾...我可以使用它通过对象池来解决问题(我认为)。跨度>
  • 您是否真的尝试过使用它并确定它对内存的使用实际上会导致问题?在我看来,您似乎只是假设它会导致 GC 问题。似乎您应该尝试一下,然后如果您遇到性能问题,请分析您的代码并查看问题所在。我强烈怀疑这不会成为问题。
  • 是的,在 Unity 中,有一个物理循环以每秒约 50 次迭代的速度运行……我尝试在每次迭代中添加 1000 个任务并获得了性能提升……但是垃圾收集器被触发了每 1-2 秒一次。 Unity 中的垃圾收集器有时会导致帧率小幅下降……尤其是在不支持 mono 的平台上,该问题的代码是自动编译的。

标签: c# multithreading concurrency queue


【解决方案1】:

我设法通过重用SingleLinkNode&lt;T&gt; 的实例来最小化垃圾收集。

这是我自定义的非阻塞堆栈:

using System.Threading;

public class LockFreeLinkPool<T> {

    private SingleLinkNode<T> head;

    public LockFreeLinkPool() {
        head = new SingleLinkNode<T>();
    }

    public void Push(SingleLinkNode<T> newNode) {
        newNode.Item = default(T);
        do {
            newNode.Next = head.Next;
        } while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, newNode.Next, newNode));
        return;
    }

    public bool Pop(out SingleLinkNode<T> node) {
        do {
            node = head.Next;
            if (node == null) {
                return false;
            }
        } while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, node, node.Next));
        return true;
    }
}

我在存储前后仔细清洁了所有实例SingleLinkNode。这对我们在 Unity 中使用线程有一些非常酷的暗示。

【讨论】:

  • 嗯...重复使用链接会让我遇到 ABA 问题吗?
  • 是的...这引入了 ABA 问题,因为链接必须根据引用相等性进行比较:-(
  • 从头开始......当一个线程正在处理的元素可以被其他线程读取+删除+重新引入时,就会发生 ABA 问题。在我使用链接池的情况下,线程在使用它们时对链接具有独占访问权。这让我很高兴:-)
猜你喜欢
  • 2017-06-11
  • 2021-01-17
  • 1970-01-01
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多