Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/286.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#_.net_Producer Consumer - Fatal编程技术网

C# 使用多个元素阻止集合?

C# 使用多个元素阻止集合?,c#,.net,producer-consumer,C#,.net,Producer Consumer,生产者-消费者集合[1][2]的所有C#实现似乎都有一个模糊相似的接口: private Queue<T> items; public void Produce(T item) public T Consume() 专用队列项目; 公共产品(T项) 公共消费 有没有像下面这样的实现 private Queue<T> items; public void Produce(T[] item) public T[] Consume(int count) 专用队列项目;

生产者-消费者集合[1][2]的所有C#实现似乎都有一个模糊相似的接口:

private Queue<T> items;

public void Produce(T item)
public T Consume()
专用队列项目;
公共产品(T项)
公共消费
有没有像下面这样的实现

private Queue<T> items;

public void Produce(T[] item)
public T[] Consume(int count)
专用队列项目;
公共无效产品(T[]项)
公共T[]消耗(整数计数)
希望这能让我一次生产/消费不同数量的物品,而不需要过多的每件物品锁定。这对于生产/消费大量项目似乎是必要的,但我还没有找到任何实现

[1]

[2]

希望这能让我一次生产/消费不同数量的物品,而不需要过多的每件物品锁定


你可以使用这个类;尽管它没有添加或获取多个项的方法,但它在内部不使用锁。

根据您想要实现的具体内容,有多种可能的方法

这里有接口的实现。据我所知,.NET framework中此接口的唯一线程安全实现是

此类允许您拥有阻塞或非阻塞生产者和消费者。通过在构造函数中为集合提供容量限制,生产者端在阻塞和非阻塞之间设置。正如该方法的文件所述:

如果在初始化此实例时指定了有界容量,则Add调用可能会阻塞,直到有空间存储提供的项为止

对于获取项目,您可以使用不同的
Take
TryTake
方法,或者使用非常方便的方法创建
IEnumerable
,创建
IEnumerator
,该方法在获取下一个值时使用
BlockingCollection
中的一个元素,并在源集合中阻塞是空的。直到调用并且集合不接受任何新数据为止。此时,所有使用可枚举实例的实例都将停止阻塞,并报告不再有数据(只要所有剩余数据都已使用)

因此,您基本上可以实现这样的消费者:

BlockingCollection<...> bc = ...
foreach (var item in bc.GetConsumingEnumerable())
{
    // do something with your item
}
BlockingCollection bc=。。。
foreach(bc.GetConsumingEnumerable()中的变量项)
{
//对你的物品做点什么
}
这样的使用者可以在多个线程中启动,因此如果您选择,您可以有多个线程从源代码中读取。您可以创建任意数量的使用枚举

您应该知道,这个集合实际上只是一个包装器。有一个构造函数允许您设置使用的集合类型。默认情况下。这意味着默认情况下集合的行为类似于此队列,并且是先进先出集合,以防您只使用一个生产者和一个消费者


尽管如此,还是有另一种选择。如果您不需要阻塞部分(或者您想自己实现阻塞部分),如果您不需要集合中元素的任何顺序,那么就有了。此集合非常有效地处理来自多个线程的访问。它在包装中使用较小的集合。因此,每个线程都使用自己的存储,只有当线程自身存储中的项目用完时,它才会开始从另一个线程存储中获取项目


在您的用例中,使用此集合可能会使用例的生成和消费顺序变得有趣。因此,您首先添加所有项目,一旦完成,您将使用所有项目,这两个项目都有多个线程。

正是出于这一需要,我自己创建了一个方法扩展。请注意,如果至少从队列中删除了一个元素,则此调用将记录任何其他异常,并返回这一个元素以防止丢失任何内容

public static class BlockingCollectionMethodExtensions
{
    public static List<T> FetchAtLeastOneBlocking<T>(this BlockingCollection<T> threadSafeQueue, int maxCount, ICommonLog log)
    {
        var resultList = new List<T>();

        // Take() will block the thread until new elements appear
        // It will also throw an InvalidOperationException when blockingCollection is Completed
        resultList.Add(threadSafeQueue.Take());

        try
        {
            // Fetch more unblocking
            while (threadSafeQueue.Count > 0 && resultList.Count < maxCount)
            {
                T item;
                bool success = false;
                success = threadSafeQueue.TryTake(out item);
                if (success)
                {
                    resultList.Add(item);
                }
                else
                {

                }
            }
        }
        catch (Exception ex)
        {
            log.Fatal($"Unknown error fetching more elements. Continuing to process the {resultList.Count} already fetched items.", ex);
        }

        return resultList;
    }
}
公共静态类BlockingCollectionMethodExtensions
{
公共静态列表FetchAtlastoneBlocking(此BlockingCollection threadSafeQueue、int-maxCount、ICommonLog日志)
{
var resultList=新列表();
//Take()将阻止线程,直到出现新元素
//当blockingCollection完成时,它还将抛出InvalidOperationException
Add(threadSafeQueue.Take());
尝试
{
//获取更多解锁
while(threadSafeQueue.Count>0&&resultList.Count
以及相应的测试:

public class BlockingCollectionMethodExtensionsTest : UnitTestBase
{
    [Fact]
    public void FetchAtLeastOneBlocking_FirstEmpty_ThenSingleEntryAdded_ExpectBlocking_Test()
    {
        var queue = new BlockingCollection<int>();

        var startEvent = new ManualResetEvent(initialState: false);
        var completedEvent = new ManualResetEvent(initialState: false);
        List<int> fetchResult = null;

        var thread = new Thread(() =>
        {
            startEvent.Set();
            fetchResult = queue.FetchAtLeastOneBlocking<int>(maxCount: 3, log: null);
            completedEvent.Set();
        });
        thread.Start();

        var startedSuccess = startEvent.WaitOne(TimeSpan.FromSeconds(2)); // Wait until started
        Assert.True(startedSuccess);

        // Now wait for 2 seconds to ensure that nothing will be fetched
        Thread.Sleep(TimeSpan.FromSeconds(1));
        Assert.Null(fetchResult);

        // Add a new element and verify that the fetch method succeeded
        queue.Add(78);

        var completedSuccess = completedEvent.WaitOne(timeout: TimeSpan.FromSeconds(2));
        Assert.True(completedSuccess);
        Assert.NotNull(fetchResult);
        Assert.Single(fetchResult);
        Assert.Equal(78, fetchResult.Single());
    }

    [Fact]
    public void FetchAtLeastOneBlocking_FirstEmpty_ThenCompleted_ExpectOperationException_Test()
    {
        var queue = new BlockingCollection<int>();
        Exception catchedException = null;

        var startEvent = new ManualResetEvent(initialState: false);
        var exceptionEvent = new ManualResetEvent(initialState: false);
        List<int> fetchResult = null;

        var thread = new Thread(() =>
        {
            startEvent.Set();
            try
            {
                fetchResult = queue.FetchAtLeastOneBlocking<int>(maxCount: 3, log: null);
            }
            catch (Exception ex)
            {
                catchedException = ex;
                exceptionEvent.Set();

            }
        });
        thread.Start();

        var startedSuccess = startEvent.WaitOne(TimeSpan.FromSeconds(2)); // Wait until started
        Assert.True(startedSuccess);

        // Now wait for 2 seconds to ensure that nothing will be fetched
        Thread.Sleep(TimeSpan.FromSeconds(1));
        Assert.Null(fetchResult);

        // Now complete the queue and assert that fetching threw the expected exception
        queue.CompleteAdding();

        // Wait for the exception to be thrown
        var exceptionSuccess = exceptionEvent.WaitOne(TimeSpan.FromSeconds(2));
        Assert.True(exceptionSuccess);
        Assert.NotNull(catchedException);
        Assert.IsType<InvalidOperationException>(catchedException);
    }

    [Fact]
    public void FetchAtLeastOneBlocking_SingleEntryExists_ExpectNonblocking_Test()
    {
        var queue = new BlockingCollection<int>();
        // Add a new element and verify that the fetch method succeeded
        queue.Add(78);

        var startEvent = new ManualResetEvent(initialState: false);
        var completedEvent = new ManualResetEvent(initialState: false);
        List<int> fetchResult = null;

        var thread = new Thread(() =>
        {
            startEvent.Set();
            fetchResult = queue.FetchAtLeastOneBlocking<int>(maxCount: 3, log: null);
            completedEvent.Set();
        });
        thread.Start();

        var startedSuccess = startEvent.WaitOne(TimeSpan.FromSeconds(2)); // Wait until started
        Assert.True(startedSuccess);

        // Now wait for expected immediate completion
        var completedSuccess = completedEvent.WaitOne(timeout: TimeSpan.FromSeconds(2));
        Assert.True(completedSuccess);
        Assert.NotNull(fetchResult);
        Assert.Single(fetchResult);
        Assert.Equal(78, fetchResult.Single());
    }

    [Fact]
    public void FetchAtLeastOneBlocking_MultipleEntriesExist_ExpectNonblocking_Test()
    {
        var queue = new BlockingCollection<int>();
        // Add a new element and verify that the fetch method succeeded
        queue.Add(78);
        queue.Add(79);

        var startEvent = new ManualResetEvent(initialState: false);
        var completedEvent = new ManualResetEvent(initialState: false);
        List<int> fetchResult = null;

        var thread = new Thread(() =>
        {
            startEvent.Set();
            fetchResult = queue.FetchAtLeastOneBlocking<int>(maxCount: 3, log: null);
            completedEvent.Set();
        });
        thread.Start();

        var startedSuccess = startEvent.WaitOne(TimeSpan.FromSeconds(2)); // Wait until started
        Assert.True(startedSuccess);

        // Now wait for expected immediate completion
        var completedSuccess = completedEvent.WaitOne(timeout: TimeSpan.FromSeconds(2));
        Assert.True(completedSuccess);
        Assert.NotNull(fetchResult);
        Assert.Equal(2, fetchResult.Count);
        Assert.Equal(78, fetchResult[0]);
        Assert.Equal(79, fetchResult[1]);
    }

    [Fact]
    public void FetchAtLeastOneBlocking_MultipleEntriesExist_MaxCountExceeded_ExpectNonblocking_Test()
    {
        var queue = new BlockingCollection<int>();
        // Add a new element and verify that the fetch method succeeded
        queue.Add(78);
        queue.Add(79);
        queue.Add(80);
        queue.Add(81);

        var startEvent = new ManualResetEvent(initialState: false);
        var completedEvent = new ManualResetEvent(initialState: false);
        List<int> fetchResult = null;

        var thread = new Thread(() =>
        {
            startEvent.Set();
            fetchResult = queue.FetchAtLeastOneBlocking<int>(maxCount: 3, log: null);
            completedEvent.Set();
        });
        thread.Start();

        var startedSuccess = startEvent.WaitOne(TimeSpan.FromSeconds(2)); // Wait until started
        Assert.True(startedSuccess);

        // Now wait for expected immediate completion
        var completedSuccess = completedEvent.WaitOne(timeout: TimeSpan.FromSeconds(2));
        Assert.True(completedSuccess);
        Assert.NotNull(fetchResult);
        Assert.Equal(3, fetchResult.Count);
        Assert.Equal(78, fetchResult[0]);
        Assert.Equal(79, fetchResult[1]);
        Assert.Equal(80, fetchResult[2]);
    }
}
公共类BlockingCollectionMethodExtensionTest:UnitTestBase { [事实] public void FetchAtLeastOneBlocking\u FirstEmpty\u然后singleentryadded\u ExpectBlocking\u Test() { var queue=new BlockingCollection(); var startEvent=新手动重置事件(初始状态:false); var completedEvent=新的手动重置事件(初始状态:false); List fetchResult=null; 变量线程=新线程(()=> { startEvent.Set(); fetchResult=queue.FetchAtlastoneBlocking(maxCount:3,log:null); completedEvent.Set(); }); thread.Start(); var startedSuccess=startEvent.WaitOne(TimeSpan.FromSeconds(2));//等待启动 断言.True(startedSuccess); //现在等待2秒钟以确保不会提取任何内容 线程睡眠(TimeSpan.FromSeconds(1)); Assert.Null(fetchResult); //添加一个新的元素