【问题标题】:Parallel tasks with a long pause长时间停顿的并行任务
【发布时间】:2012-10-15 00:58:06
【问题描述】:

我有一个类似的功能

   private void DoSomethingToFeed(IFeed feed)
   {
      feed.SendData(); // Send data to remote server
      Thread.Sleep(1000 * 60 * 5); // Sleep 5 minutes
      feed.GetResults(); // Get data from remote server after it's processed it
   }

我想并行化这个,因为我有很多相互独立的提要。基于this answer,将Thread.Sleep() 留在其中并不是一个好主意。我还想在所有线程都启动后等待,直到它们都有机会获得结果。

处理这种情况的最佳方法是什么?

编辑,因为我不小心把它漏掉了:我本来考虑把这个函数称为Parallel.ForEach(feeds, DoSomethingToFeed),但是当我找到答案时我想知道是否有更好的方法来处理睡眠我链接到了。

【问题讨论】:

  • 不要并行化睡眠调用。使用计时器。
  • @HansPassant - 这正是我想弄清楚如何避免的。
  • @Bobson 为什么要避免这种情况?
  • 你能用 C# 5 吗?您链接到的问题专门讨论了Parallel.For(),使用Thread.Sleep() 在您的情况下可能就足够了,具体取决于您使用它的方式和您的其他要求。
  • @svick - 我指的是避免并行睡眠调用。唔。我想我已经提到当我找到那个线程时我已经在看Parallel.ForEach(),但我在那里看不到它。一定从来没有从我的头到键盘。不幸的是,C# 5 不是一个选项。

标签: c# multithreading parallel-processing task-parallel-library


【解决方案1】:

我认为您应该看看 .NET 中的 Task 类。这是在更底层线程/线程池管理之上的一个很好的抽象。

为了等待所有任务完成,您可以使用Task.WaitAll

Tasks 的用法示例如下:

IFeed feedOne = new SomeFeed();
IFeed feedTwo = new SomeFeed();

var t1 = Task.Factory.StartNew(() => { feedOne.SendData(); });

var t2 = Task.Factory.StartNew(() => { feedTwo.SendData(); });

// Waits for all provided tasks to finish execution
Task.WaitAll(t1, t2);

但是,另一种解决方案是使用Parallel.ForEach,它会为您处理所有Task 创建并执行适当的任务批处理。 here 对这两种方法进行了很好的比较 - 其中指出:

Parallel.ForEach 在内部使用分区器将您的集合分配到工作项中。它不会对每个项目执行一项任务,而是将其批处理以降低所涉及的开销。

【讨论】:

  • 所以基本上,“对于简单的Parallel 来说太复杂了,请改用Task”?
  • 就个人而言,当我必须处理一组任务时,我使用Parallel.ForEach(它比自己创建和启动多个任务要快)。在 Parallel.ForEach 不合适的情况下,我使用 Tasks 代替。
  • 你将如何处理等待?
  • 如果任务很长,线程池会很快耗尽,你最终会等待很多时间。在这种情况下使用线程池不是一个好主意。
  • @zmbq 首先 - 在原始问题中它说:“我还想在所有线程都启动后等待,直到它们都有机会获得结果。” - 这就是我要回答的问题。在编辑过的问题中,他最好为每个任务使用一些回调并在其中包含一些逻辑,例如,当所有任务完成时触发一个事件。这样你就不会阻塞任何东西,当它准备好时你就会得到结果。
【解决方案2】:

检查 WaitHandle 等待任务。

【讨论】:

  • 在这种情况下你会如何使用它?
  • 正如@LasseChristiansen 解释的那样
【解决方案3】:

除非你有很多线程,否则你可以保持简单。创建所有线程。您将获得一些线程创建开销,但由于线程基本上一直处于休眠状态,您不会获得太多的上下文切换。

它比任何其他解决方案都更容易编码(除非您使用的是 C# 5)。所以从这个开始,只有当你真正看到性能问题时才改进它。

【讨论】:

  • 所以,基本上,让线程都调用Thread.Sleep(),然后被分页?
  • 线程不会被“分页”,内存会。线程将只是休眠并且在它们醒来之前不会参与上下文切换。如果您没有数千个,这应该可以正常工作。
  • 这可能不是 最好的 答案,但我喜欢它,因为它很简单。休眠线程不会阻塞其他线程启动?
  • 进程中的线程数没有真正的限制,所以没有。如果您使用线程池,则线程数会受到限制。
【解决方案4】:
private void DoSomethingToFeed(IFeed feed)
{
    Task.Factory.StartNew(() => feed.SendData())
        .ContinueWith(_ => Delay(1000 * 60 * 5)
                          .ContinueWith(__ => feed.GetResults())
                     );
}

//http://stevenhollidge.blogspot.com/2012/06/async-taskdelay.html
Task Delay(int milliseconds)      
{
    var tcs = new TaskCompletionSource<object>();
    new System.Threading.Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
    return tcs.Task;
}

【讨论】:

  • 为什么这回答了 OP 的问题?请解释一下。
  • @theTinMan 我不知道,我只是输入了随机字符。
  • 再想想,一些解释会很有用。我应该在Parallel.ForEach() 中调用它吗?我是从普通的foreach 执行此操作吗?有关系吗?
  • @Bobson 不需要Parallel.ForEach,因为这个方法已经创建了任务(并且执行SendData的线程不必与执行GetResults的线程相同(
  • 你为什么要使用Task.Delay().Wait()?如果您使用的是 .Net 4.5,不妨使用await
猜你喜欢
  • 2012-05-11
  • 2012-12-12
  • 1970-01-01
  • 2019-05-29
  • 1970-01-01
  • 2020-10-17
  • 2014-02-27
  • 2021-10-15
  • 1970-01-01
相关资源
最近更新 更多