何时使用Task.Delay,何时使用Thread.Sleep?

时间:2013-11-19 21:16:49

标签: c# multithreading task-parallel-library

何时使用Task.DelayThread.Sleep有合适的规则?

  • 具体而言,是否有一个最小值来提供一个有效/高效的另一个?
  • 最后,由于Task.Delay导致异步/等待状态机上下文切换,是否有使用它的开销?

7 个答案:

答案 0 :(得分:294)

如果要阻止当前线程,请使用Thread.Sleep

如果您想要逻辑延迟而不阻止当前线程,请使用Task.Delay

效率不应成为这些方法的首要考虑因素。它们的主要实际用途是作为I / O操作的重试计时器,其大小为秒而不是毫秒。

答案 1 :(得分:189)

Task.DelayThread.Sleep之间的最大区别是Task.Delay旨在异步运行。在同步代码中使用Task.Delay没有意义。在异步代码中使用Thread.Sleep是一个非常糟糕的主意。

通常,您会使用Task.Delay()关键字致电await

await Task.Delay(5000);

或者,如果你想在延迟之前运行一些代码:

var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;

猜猜这会打印什么?运行0.0070048秒。 如果我们将await delay移到Console.WriteLine之上,它将打印运行5.0020168秒。

让我们看一下与Thread.Sleep的区别:

class Program
{
    static void Main(string[] args)
    {
        Task delay = asyncTask();
        syncCode();
        delay.Wait();
        Console.ReadLine();
    }

    static async Task asyncTask()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("async: Starting");
        Task delay = Task.Delay(5000);
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        await delay;
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("async: Done");
    }

    static void syncCode()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("sync: Starting");
        Thread.Sleep(5000);
        Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("sync: Done");
    }
}

尝试预测会打印什么......

  

异步:开始
  异步:运行0.0070048秒
  同步:开始
  异步:运行5.0119008秒
  异步:完成
  同步:运行5.0020168秒
  同步:完成

另外,有趣的是注意Thread.Sleep更准确,ms准确度不是真正的问题,而Task.Delay可能需要15-30ms。两个函数的开销与它们具有的ms精度相比是最小的(如果您需要更精确的东西,请使用Stopwatch类)。 Thread.Sleep仍然绑定你的主题,Task.Delay释放它以便在你等待时做其他工作。

答案 2 :(得分:26)

如果当前线程被终止并且您使用Thread.Sleep并且它正在执行,那么您可能会获得ThreadAbortException。 使用Task.Delay,您始终可以提供取消令牌并优雅地将其删除。这就是我选择Task.Delay的一个原因。见http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

我也同意效率在这种情况下并不是最重要的。

答案 3 :(得分:20)

我想补充一些东西。 实际上,Task.Delay是基于计时器的等待机制。如果您查看source,您会找到对Timer类的引用,该类负责延迟。另一方面,Thread.Sleep实际上使当前线程处于休眠状态,这样你只是阻塞并浪费一个线程。在异步编程模型中,如果你想在某些延迟后发生某些事情(延续),你应该总是使用Task.Delay()

答案 4 :(得分:1)

Delayed 将是 Task.Delay 的更好名称 - 因为它不会延迟现有任务,而是创建一个新的“延迟”任务,另一方面可以等待并导致暂停到当前任务的正文。它本质上是一个计时器,但没有回调/主体。

等待延迟任务会在异步消息队列中创建一个新项目,并且不会阻塞任何线程。调用 await 的同一线程将继续处理其他任务(如果有的话),并在超时后(或队列中的前面的项目完成时)返回到等待点。引擎盖下的任务使用线程 - 可以在单个线程中调度和执行许多任务。另一方面,如果您碰巧调用 Thread.Sleep(),线程将被阻塞,即它将在请求的时间内停止运行,并且不会处理队列中的任何异步消息。

在 .NET 中,有两种主要的并行方法。旧的有线程、线程池等。新的基于任务、异步/等待、TPL。根据经验,您不要混合使用这两个领域的 API。

答案 5 :(得分:0)

还值得一提的是,Thread.Sleep(1) 会更快地触发 GC。

<块引用>

这纯粹是基于我和团队成员的观察。让我们假设 你有为特定请求创建新任务的服务 (大约 200-300 正在进行)并且此任务包含许多弱引用 在流动。任务像状态机一样工作,所以我们正在触发 Thread.Sleep(1) 改变状态,通过这样做我们设法优化 应用程序中的内存利用率 - 就像我之前说的 - 这 将使 GC 启动得更快。它没有太大区别 低内存消耗服务 (<1GB)。

答案 6 :(得分:-3)

我的看法,

Task.Delay()是异步的。它不会阻止当前线程。您仍然可以在当前线程中执行其他操作。它返回Task返回类型(Thread.Sleep()不返回任何值)。在另一个耗时的过程之后,您可以稍后检查此任务是否完成(使用Task.IsCompleted属性。

Thread.Sleep()没有返回类型。它是同步的。在线程中,除了等待延迟完成之外,您什么都做不到。

对于现实生活中的使用,我已经编程15年了。我从未在生产代码中使用Thread.Sleep()。我找不到任何用例。 也许是因为我主要从事Web应用程序开发。