【问题标题】:How to concurrently take items from the BlockingCollection?如何同时从 BlockingCollection 中获取项目?
【发布时间】:2017-03-27 20:41:25
【问题描述】:

我有一个BlockingCollection 的整数。 下面是我正在尝试开发的代码,用于同时从 BlockingCollection 中删除项目。

static void Produce()
    {
        for (int i = 0; i < 100000; i++)
        {
            bc3.Add(i);
        }
        Run();
    }

    static void Run()
    {
        for (int i = 0; i < 5; i++)
        {
            Task.Factory.StartNew(Process, TaskCreationOptions.LongRunning);
        }
    }

    static void Process()
    {
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        while (bc3.Count!= 0)
        {
            bc3.Take();
        }
        stopWatch.Stop();
        Console.WriteLine("Thread {0}",
        Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Elapsed Time {0}", stopWatch.Elapsed.Seconds);

    }

这是通过创建 5 个任务以更快的方式删除项目的正确方法吗?

【问题讨论】:

  • 5 个任务?在我的机器上需要 16 毫秒。任务 1 甚至在 Windows 能够启动任何其他线程之前清理集合。
  • 您的代码似乎假定所有项目在您开始删除之前都已添加到集合中。如果是这种情况,那么 ConcurrentQueue 将比 BlockingCollection 执行得更好

标签: c# multithreading multitasking blockingcollection


【解决方案1】:

秒表问题

你的测量结果是错误的,因为你使用

stopWatch.Elapsed.Seconds

而不是

stopWatch.ElapsedMilliseconds

您只显示秒,而忽略分钟、小时等。

并发问题

不,这不是从 BlockingCollection 中删除项目的正确方法。不起作用的语句是

bc3.Count != 0

所有 5 个任务可能同时检查这个条件,发现还剩下 1 个项目。他们5个都去

bc3.Take();

一个任务能够移除该项目,其他 4 个任务正在等待。

解决这个问题的一种方法是添加

bc3.CompleteAdding();

Produce().

垃圾回收问题

一旦第一个任务完成,Run() 方法就会完成,并且该方法中的所有任务都会超出范围并被垃圾回收。这可能会让您只看到 1 条而不是 5 条完成消息。

要解决此问题,请使用

    static void Run()
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 5; i++)
        {
            tasks.Add(Task.Factory.StartNew(Process, TaskCreationOptions.LongRunning));
        }
        try
        {
            Task.WaitAll(tasks.ToArray());
        }
        catch (AggregateException)
        { }        
    }

同步成本

这是我的输出之一,包含 5 个任务(和 100000000 个项目):

Thread 11
Thread 13
Thread 12
Thread 14
Thread 15
Elapsed Time 12878
Elapsed Time 13122
Elapsed Time 13128
Elapsed Time 13128
Elapsed Time 13128
Run: 13140

将此与单个任务进行比较:

Thread 12
Elapsed Time 10147
Run: 10149

这是因为只有一个 Take() 方法可以删除一个项目,并且同步需要额外的时间。

【讨论】:

  • 完全正确,但我认为一旦将其搭建到位,每个消费者所做的实际工作都会掩盖同步开销,但 +1 用于覆盖所有基地。
  • Once the first task finishes, the Run() method completes and all tasks in the method go out of scope and are garbage collected 这不是问题。在排队时,任务由 TaskScheduler 引用。然后在运行时它也不是问题,因为线程是植根的。即使任务实例被收集,也不会妨碍工作的完成(假设程序没有在结束前退出)
  • @KevinGosse:我最初也是这么想的,但我从未在屏幕上看到过剩余任务的 Console.Writeline(),即使我将它们放入 finally 块中。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-17
  • 1970-01-01
相关资源
最近更新 更多