Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.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_Task Parallel Library_Multitasking - Fatal编程技术网

C# 高效地等待一个或多个资源可用

C# 高效地等待一个或多个资源可用,c#,multithreading,task-parallel-library,multitasking,C#,Multithreading,Task Parallel Library,Multitasking,在我花太长时间重新发明轮子之前,我想检查一下.Net中是否已经有一个类可以满足我的需要 我想要的是有点像信号灯(或者甚至像倒计时事件),但有点不同 我有一个需求,我有不同数量的“资源”可用,我希望线程在没有可用资源时高效地等待。同时,另一个线程可以释放一个资源,这将立即释放另一个等待的线程 这听起来很像一个信号量,但这并不是因为信号量(据我所见)将每个线程视为计数的“资源” 无论如何,这是我想要的第一个简单实现。它还没有处理、代码契约、错误处理、超时支持或取消支持,但它应该展示我想要的: pub

在我花太长时间重新发明轮子之前,我想检查一下.Net中是否已经有一个类可以满足我的需要

我想要的是有点像信号灯(或者甚至像倒计时事件),但有点不同

我有一个需求,我有不同数量的“资源”可用,我希望线程在没有可用资源时高效地等待。同时,另一个线程可以释放一个资源,这将立即释放另一个等待的线程

这听起来很像一个信号量,但这并不是因为信号量(据我所见)将每个线程视为计数的“资源”

无论如何,这是我想要的第一个简单实现。它还没有处理、代码契约、错误处理、超时支持或取消支持,但它应该展示我想要的:

public sealed class ResourceCounter
{
    /// <summary>Create with the specified number of resources initially available.</summary>

    public ResourceCounter(int resourceCount)
    {
        _resourceCount = resourceCount;

        if (_resourceCount > 0)
        {
            _resourceAvailable.Set();
        }
    }

    /// <summary>Acquires a resource. Waits forever if necessary.</summary>

    public void Acquire()
    {
        while (true)
        {
            _resourceAvailable.Wait();

            lock (_lock)
            {
                if (_resourceCount > 0)
                {
                    if (--_resourceCount == 0)
                    {
                        _resourceAvailable.Reset();
                    }

                    return;
                }
            }
        }
    }

    /// <summary>Releases a resource.</summary>

    public void Release()
    {
        lock (_lock)
        {
            ++_resourceCount;
            _resourceAvailable.Set();
        }
    }

    private int _resourceCount;
    private readonly object _lock = new object(); 
    private readonly ManualResetEventSlim _resourceAvailable = new ManualResetEventSlim();
}
公共密封类资源计数器
{
///使用指定数量的初始可用资源创建。
公共资源计数器(int resourceCount)
{
_resourceCount=resourceCount;
如果(_resourceCount>0)
{
_resourceAvailable.Set();
}
}
///获取资源。如果需要,将永远等待。
公开无效获取()
{
while(true)
{
_resourceAvailable.Wait();
锁
{
如果(_resourceCount>0)
{
如果(---U resourceCount==0)
{
_Reset();
}
返回;
}
}
}
}
///释放资源。
公开无效释放()
{
锁
{
++_资源计数;
_resourceAvailable.Set();
}
}
私人内部资源计数;
私有只读对象_lock=新对象();
私有只读手册ResetEventSlim\u resourceAvailable=新手册ResetEventSlim();
}
使用模式非常简单:

  • 使用所需的初始资源计数(可以是零或更多)构造ResourceCounter

  • 想要获取资源的线程调用ResourceCounter.acquire(),该线程在资源可用且已获取之前不会返回

  • 想要释放资源的线程调用ResourceCounter.release(),它将释放资源并立即返回

  • 请注意,任何线程都可以释放资源;它不一定是获得资源的人

    我使用它作为一些多线程管道代码的一部分,其中一个线程负责将工作项排队,几个线程处理工作项,另一个线程输出已处理的工作项。输出已处理工作项的线程必须对它们进行多路复用(因为处理线程可能以任何顺序输出已完成的项),并且我需要一种机制来阻止工作项在多路复用器等待延迟项时无休止地排队

    (有关这方面的一些背景信息,请参见。)

    不管怎样,有什么可以做的,或者我应该继续为它开发我自己的类吗


    [编辑]


    如下所述,信号量lim做的事情完全正确。我拒绝了它,因为我认为调用Wait()的线程必须是调用Release()的线程,但事实并非如此。这就是我在星期天编码得到的结果…;)

    使用队列通信更容易构建多级管道体系结构。生产者线程将项目放入工作队列,一个或多个工作线程将项目出列并处理,然后将它们添加到输出队列。最后一个线程读取输出队列并输出数据

    在.NET中,这很容易通过使用来实现

    有关两级管道的示例,请参见。添加另一个阶段很简单

    为了处理输出线程使事情无序的问题,我使用最小堆将输出队列设置为优先级队列。我的项目由顺序记录编号标识,因此输出线程知道下一个要输出的记录编号。它将在
    AutoResetEvent
    上等待一个项目放入队列(工作进程将在项目排队时设置事件)。然后,输出线程将查看顶部的项,以查看它是否与预期的项匹配。如果没有,它将再次等待事件

    这非常有效,因为它消除了第二个队列。该块位于它所属的输出队列中。表演对我来说非常好。将项目排队是一个O(logn)操作,但实际上
    n
    非常小。即使队列中有100000个项目,与处理记录所需的时间相比,项目排队所需的时间也微不足道

    您仍然可以为此使用
    BlockingCollection
    。您只需要让一个二进制堆类实现
    IProducerConsumerCollection
    接口。我是通过向我在中发布的简单二进制堆类添加锁来实现的。然后,您可以向
    BlockingCollection
    构造函数提供其中一个,如下所示:

    BlockingCollection<MyRecord> = 
        new BlockingCollection<ConcurrentBinaryHeap<MyRecord>>(
        new ConcurrentBinaryHeap<MyRecord>, MaxQueueSize);
    
    BlockingCollection=
    新BlockingCollection(
    新的ConcurrentBinaryHeap,MaxQueueSize);
    
    不过,这里有一个潜在的僵局。如果队列已满(即超过初始化
    BlockingCollection
    时设置的最大值),则延迟线程无法将该项排队,所有工作将完全停止。这在实践中从未发生在我身上,因为尽管我的每记录处理时间有所不同,但变化并不大

    如果这是一个问题,您可以增加队列大小(只有在您可以肯定地说您永远不会填满队列时才有效),或者