是否有很好的规则来说明何时使用Task。Delay vs . Thread.Sleep?
具体来说,是否存在一个最小值来保证其中一个比另一个更有效? 最后,自从任务。延迟导致异步/等待状态机上的上下文切换,是否有使用它的开销?
是否有很好的规则来说明何时使用Task。Delay vs . Thread.Sleep?
具体来说,是否存在一个最小值来保证其中一个比另一个更有效? 最后,自从任务。延迟导致异步/等待状态机上的上下文切换,是否有使用它的开销?
当前回答
我和一位同事为此争论了很长时间,他向我证明,在上面的答案目前所显示的范围之外,还有显著的差异。如果你等待Task.Delay(somemillisecseconds),你实际上可以释放堆栈上的直接父对象之外的调用者:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Started " + Thread.CurrentThread.ManagedThreadId);
DoSomething1();
Console.WriteLine("Finished " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(6000);
}
static async void DoSomething1()
{
Console.WriteLine("DoSomething1 Started " + Thread.CurrentThread.ManagedThreadId);
var result = await DoSomething2();
Console.WriteLine("DoSomething1 Finished " + Thread.CurrentThread.ManagedThreadId);
}
static async Task<int> DoSomething2()
{
Console.WriteLine("DoSomething2 Started " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(5000); // Will block DoSomething1 but release Main
//Thread.Sleep(5000); // Will block everything including Main
//await Task.FromResult(5); // Will return immediately (just for comparison)
//await Task.Delay(0); // What will it do, can you guess?
Console.WriteLine("DoSomething2 Finished " + Thread.CurrentThread.ManagedThreadId);
return 0;
}
}
}
玩一下这段代码,观察使用Delay或Sleep的不同效果。这个解释超出了这个答案的范围,但可以总结为“异步函数不会启动另一个线程,直到它们等待一些不能立即运行的东西(或结果确定)”。输出如下:
Started 1
DoSomething1 Started 1
DoSomething2 Started 1
Finished 1
DoSomething2 Finished 4
DoSomething1 Finished 4
这不是关于DoSomething1();在Main被大火遗忘。你可以用Sleep来证明这一点。还要观察当DoSomething2从Task“返回”时。延迟,它在另一个线程上运行。
这个东西比我给它的信用要聪明得多,相信await只是开始了一个新的线程来做事情。我仍然没有假装完全理解,但上面的反直觉结果表明,在底层有更多的事情要做,而不仅仅是启动线程来运行代码。
其他回答
使用线程。当您想要阻塞当前线程时休眠。
使用await Task。延迟:当您希望在不阻塞当前线程的情况下进行逻辑延迟时。
效率不应该是这些方法的首要考虑因素。它们在现实世界中的主要用途是作为I/O操作的重试计时器,其数量级为秒而不是毫秒。
值得一提的是Thread.Sleep(1)将更快地触发GC。
This is purely based mine & team member observations. Lets assume that you have service which creates new task every for specific request (approx. 200-300 ongoing) and this task contains many weak references in flow. The task is working like state machine so we were firing the Thread.Sleep(1) on change state and by doing so we managed to optimize utilization of memory in the application - like I said before - this will makes GC to fire faster. It doesn't make so much difference in low memory consumption services (<1GB).
是的,对于何时使用Task有一些一般的指导方针。Delay vs . Thread.Sleep。的任务。延迟是c#中异步编程的首选方法,因为它允许您在不阻塞线程的情况下异步等待。线程。Sleep阻塞调用线程,并可能导致高并发性应用程序的性能问题。
Task没有最小值。延迟对Thread.Sleep有效。一般来说,如果你需要等待一定的时间,最好使用Task.Delay。如果你需要在特定的时间内阻塞一个线程,请使用thread . sleep。
至于使用Task的开销。在异步/等待状态机中,上下文切换涉及一些开销。但是,开销通常很小,而且异步编程的好处远远超过了它。当使用Task。正确地延迟,它可以帮助提高应用程序的响应性和可伸缩性。
我的观点,
Task.Delay()是异步的。它不会阻塞当前线程。您仍然可以在当前线程中执行其他操作。它返回一个Task返回类型(Thread.Sleep()不返回任何东西)。您可以检查该任务是否已完成(使用task。IsCompleted属性),稍后在另一个耗时的过程之后。
Thread.Sleep()没有返回类型。它是同步的。在线程中,除了等待延迟完成之外,您实际上不能做任何事情。
至于在现实生活中的使用,我已经编程15年了。我从未在产品代码中使用过Thread.Sleep()。我找不到任何用例。 也许是因为我主要从事web应用程序开发。
在异步程序中,两者的区别
await task.Delay()
//and
thread.sleep
在一个简单的应用程序中,一个可能更可取消,一个可能更准确,一个可能更快……但在一天结束的时候,两者都做同样的事情,他们阻塞正在执行的代码…
以下是调查结果:
1 00:00:00.0000767
Not Delayed.
1 00:00:00.2988809
Delayed 1 second.
4 00:00:01.3392148
Delayed 3 second.
5 00:00:03.3716776
Delayed 9 seconds.
5 00:00:09.3838139
Delayed 10 seconds
4 00:00:10.3411050
4 00:00:10.5313519
从这段代码:
var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"{sw.Elapsed}");
var asyncTests = new AsyncTests();
var go1 = asyncTests.WriteWithSleep();
var go2 = asyncTests.WriteWithoutSleep();
await go1;
await go2;
sw.Stop();
Console.WriteLine($"{sw.Elapsed}");
Stopwatch sw1 = new Stopwatch();
Stopwatch sw = new Stopwatch();
public async Task WriteWithSleep()
{
sw.Start();
var delayedTask = Task.Delay(1000);
Console.WriteLine("Not Delayed.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
await delayedTask;
Console.WriteLine("Delayed 1 second.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
Thread.Sleep(9000);
Console.WriteLine("Delayed 10 seconds");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
sw.Stop();
}
public async Task WriteWithoutSleep()
{
await Task.Delay(3000);
Console.WriteLine("Delayed 3 second.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
await Task.Delay(6000);
Console.WriteLine("Delayed 9 seconds.");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} {sw.Elapsed}");
}
Sleep acts the same way as an immediate await, except it blocks the thread. A task that is assigned to a var may cause a thread switch when it is finally awaited. In this example, it looks like the code starts on thread 1, then creates thread 5 for WriteWithoutSleep(), but continues executing on thread 1 for ThreadWithSleep() until delayedTask is awaited. At that moment, thread 1's code flows into thread 4 and further execution in Main is now on thread 4; thread 1 is for lack of a better word thrown away.
以上所有的答案都很有价值。然而,在一个简单的控制台应用程序中,它似乎并不重要,除非在你使用的几次运行过程中,如果你立即等待你的Task.Delay()并且不打算使用取消令牌;
在一个复杂的应用程序中,让线程进入睡眠状态,还是因为创建任务而从一个线程跳到另一个线程,然后等待它们,还是立即等待,这是需要考虑的问题。
最后,放入Process.GetCurrentProcess(). threads。控制台应用程序(至少是我的)开头的Count在调试器模式下产生了13个线程。在等待调用之后,我在visual studio的调试器模式下有17个线程。我曾经读到consoleApp只有3个线程,其余的都是调试器线程,但是在没有调试的情况下运行consoleApp会导致8个线程和14个线程。在visual studio外部运行它的结果是8个线程和14个线程。
复制代码并粘贴它之后,线程数是相同的,8,14,所有内容都停留在线程4和5上。第二个线程。Sleep和task.delay不会导致线程跳转。所有这些研究都是为了提出:while线程。Sleep会阻塞一个线程,task.delay不会,并且有一个取消令牌,除非你的应用程序非常复杂,它真的不重要,因为表面上:task.delay和thread。睡眠也做着同样的事情。