【问题标题】:Thread.Sleep into a TaskThread.Sleep 进入任务
【发布时间】:2015-12-09 17:17:04
【问题描述】:

我有以下代码:

    static void Main(string[] args)
    {
        IEnumerable<int> threadsIds = Enumerable.Range(1, 1000);
        DateTime globalStart = DateTime.Now;

        Console.WriteLine("{0:s.fff} Starting tasks", globalStart);
        Parallel.ForEach(threadsIds, (threadsId) =>
        {
            DateTime taskStart = DateTime.Now;
            const int sleepDuration = 1000;
            Console.WriteLine("{1:s.fff} Starting task {0}, sleeping for {2}", threadsId, taskStart, sleepDuration);
            Thread.Sleep(sleepDuration);
            DateTime taskFinish = DateTime.Now;
            Console.WriteLine("{1:s.fff} Ending task {0}, task duration {2}", threadsId, taskFinish, taskFinish- taskStart);

        });
        DateTime globalFinish= DateTime.Now;
        Console.WriteLine("{0:s.fff} Tasks finished. Total duration: {1}", globalFinish, globalFinish-globalStart);
        Console.ReadLine();
    }

目前,当我运行它时,运行它大约需要 60 秒。据我了解,这是因为.Net 不会为每个任务创建一个线程,而是为所有Tasks 创建一些线程,当我执行Thread.Sleep 时,我会阻止该线程执行其他一些任务。

在我的实际情况中,我有一些并行工作要做,如果失败,我必须等待一段时间才能重试。

我正在寻找除 Thread.Sleep 之外的其他东西,这将允许其他任务在其他任务的“睡眠时间”期间运行。

不幸的是,我目前正在运行 .Net 4,这使我无法使用 asyncawait(我想在这种情况下这对我有帮助。

Ps,我得到了相同的结果:

  • Task.Delay(sleepDuration).Wait()
  • 不使用Parallel.Foreach,而是使用Task.Factory.StartNew 的foreach

Ps2,我知道我可以以不同的方式处理我的真实案例,但我很想了解如何以这种方式实现它。

【问题讨论】:

  • Task.Delay.Wait() (实际上)与Thread.Sleep 相同,所以很清楚为什么它没有帮助。这个问题似乎更像是“如何重试失败的任务?”那么如何开始任务。但我想重试部分取决于您的真实代码和要求。让任务等待并重试的想法总是会导致您描述的相同结果。尝试评估每个任务的结果并重新启动失败的任务。
  • Parallel.ForEach 不是为了同时并行运行所有操作,而是为了优化执行以并行运行 someawait Task.WhenAll + await Task.Delay() 而不是 Parallel.ForEach + Thread.Sleep() 确实是一个解决方案......
  • 如果您需要等待一段时间,为什么不使用计时器并让失败的任务设置计时器以在 60 秒或其他时间再次运行您的方法,就像您不在线程池中睡觉一样.
  • 你可能想看看Task.ContinueWith。这可以使用选项调用,以便仅在第一个任务失败时才开始继续。因此,您失败的任务可能会抛出,从而再次启动(相同的方法),尽管仍然存在等待一点的问题。抱歉,无法将这些组合成一个合格的答案。
  • @J4N:如果你在桌面上运行,你可以使用Microsoft.Bcl.Async来启用asyncawait

标签: c# .net multithreading task-parallel-library


【解决方案1】:

你走在正确的道路上。 Task.Delay(timespan) 是您问题的解决方案。由于你不能使用async/await,你必须多写一点代码才能达到预期的效果。

考虑使用Task.ContinueWith()方法,例如:

Task.Run(() => { /* code before Thread.Sleep */ })
.ContinueWith(task => Task.Delay(sleepDuration)
    .ContinueWith(task2 => { /* code after Thread.Sleep */ }));

您还需要创建一个类以使本地方法变量可以跨子任务访问。

如果你想创建一个在某些条件下每秒运行一次轮询的任务,你可以试试下面的代码:

Task PollTask(Func<bool> condition)
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    PollTaskImpl(tcs, condition);
    return tcs.Task;
}

void PollTaskImpl(TaskCompletionSource<bool> tcs, Func<bool> condition)
{
    if (condition())
        tcs.SetResult(true);
    else
        Task.Delay(1000).ContinueWith(_ => PollTaskImpl(tcs, condition));
}

不用担心每秒都会创建新任务 - ContinueWithasync/await 方法在内部做同样的事情。

【讨论】:

  • 您的第一个延续是创建一个Task&lt;Task&gt;。你需要解开它,否则下一个延续会在不应该的时候立即执行。
  • 修复了问题,只是想写出更漂亮的代码。
  • 就像我说的,你应该真正解开任务,因为你仍然在这里创建一个嵌套任务,使得运行这个任务的结果更难工作。
  • 您好,谢谢您的回答。问题是我需要做一些事情而未达到某些条件:如果 subTask!=succeeded 然后休眠 1 秒并重新开始。我不明白这种 continueWith 怎么可能
  • @J4N 如果这就是你想要做的,那么你应该在你的问题中提出这个问题。这不是你的问题所要问的,所以你不应该期望答案就是这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2014-11-26
相关资源
最近更新 更多