C# 不使用.net 4.0的并发任务队列的最佳替代方案是什么

C# 不使用.net 4.0的并发任务队列的最佳替代方案是什么,c#,multithreading,concurrency,queue,C#,Multithreading,Concurrency,Queue,我正在尝试实现一个在Unity应用程序中使用多核的框架 到目前为止,我已经实现了一个由Julian M Bucknal实现的。就我的目的而言,它似乎工作得很好。我创建了一个任务类和一个结果类,并使用两个队列来分发和收集在辅助线程上处理的工作 剩下的问题是,由于这段代码将在我的应用程序的性能关键部分大量使用,因此我非常希望尽量减少垃圾收集器的使用 是否有一个已知的库/模板/技术可以帮助我进行改进 using System.Threading; public class LockFreeQueue

我正在尝试实现一个在Unity应用程序中使用多核的框架

到目前为止,我已经实现了一个由Julian M Bucknal实现的。就我的目的而言,它似乎工作得很好。我创建了一个任务类和一个结果类,并使用两个队列来分发和收集在辅助线程上处理的工作

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

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

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;
    }
}
使用系统线程;
公共类无锁队列{
内部结构SingleLinkNode,其中U:T{
//注意;下一个成员不能是属性,因为
//它参与许多CAS操作
公共SingleLinkNode-Next;
公共U项目;
}
静态私有布尔CAS(参考V位置,V比较,V新值),其中V:类{
返回
(对象)比较对象==
(对象)联锁。比较交换(参考位置、新值、比较项);
}
单链路节点头;
单节尾;
public LockFreeQueue(){
head=新的SingleLinkNode();
尾=头;
}
公共无效排队(T项){
SingleLinkNode oldTail=null;
SingleLinkNode-oldTailNext;
SingleLinkNode newNode=新的SingleLinkNode();
newNode.Item=Item;
bool newNodeWasAdded=false;
而(!newnodewadded){
老尾巴=尾巴;
oldTailNext=oldTail.Next;
if(tail==oldTail){
if(oldTailNext==null)
newNodeWasAdded=CAS(参考tail.Next,null,newNode);
其他的
CAS(参考尾、oldTail、oldTailNext);
}
}
CAS(参考尾、旧尾、新节点);
}
公共bool出列(T项外){
项目=默认值(T);
SingleLinkNode oldHead=null;
bool haveAdvancedHead=假;
而(!haveAdvancedHead){
oldHead=头;
SingleLinkNode oldTail=尾部;
SingleLinkNode oldHeadNext=oldHead.Next;
如果(旧头==头){
如果(oldHead==oldTail){
if(oldHeadNext==null){
返回false;
}
CAS(参考尾部、oldTail、oldHeadNext);
}否则{
item=oldHeadNext.item;
haveAdvancedHead=CAS(参考head、旧head、旧HeadNext);
}
}
}
返回true;
}
公共T出列(){
T结果;
退出(退出结果);
返回结果;
}
}

我通过重用
SingleLinkNode
的实例,尽量减少了垃圾收集

这是我定制的非阻塞堆栈:

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;
    }
}
使用系统线程;
公共类LockFreeLinkPool{
私有单链路节点头;
public LockFreeLinkPool(){
head=新的SingleLinkNode();
}
公共无效推送(SingleLinkNode newNode){
newNode.Item=默认值(T);
做{
newNode.Next=head.Next;
}而(!SyncMethods.CAS(ref head.Next,newNode.Next,newNode));
返回;
}
公共bool Pop(输出单链接节点){
做{
节点=头部。下一步;
if(node==null){
返回false;
}
}而(!SyncMethods.CAS(ref head.Next,node,node.Next));
返回true;
}
}

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

Hmm,如果您正在寻找迁移到.NET 4的好理由,那么您会找到一个。这个问题提得太晚了5年。我想Unity有12个理由不这么做。。。但我希望他们能。嗯。。。如果我有一个非阻塞堆栈,它不会产生太多垃圾。。。我可以用它来解决这个问题,通过使用对象池(我认为)。你是否真的尝试过使用它,并确定它对内存的使用确实会导致问题?对我来说,听起来好像你只是假设它会导致GC问题。似乎您应该尝试一下,然后如果您有性能问题,请分析您的代码并查看问题所在。我强烈怀疑这不会是一个问题。是的,在Unity中有一个物理循环,以每秒约50次迭代的速度运行。。。我尝试在每次迭代中添加1000个任务,并获得了性能提升。。。但是垃圾收集器每1-2秒触发一次。Unity中的垃圾收集器有时会导致帧速率出现小幅下降。。。在不支持mono的平台上,这尤其是一个问题,因为它的代码是aot编译的。嗯。。。重复使用链接会让我面临ABA问题吗?是的。。。这引入了ABA问题,因为链接必须基于引用相等进行比较:-(请注意……当线程正在处理的元素可以被其他线程读取+删除+重新引入时,就会出现ABA问题。在我的链接池中,线程在处理链接时可以独占访问链接。这让我很高兴:-)