var task = Task.Run(() => { Thread.Sleep(1000); return "Hello World"; }); var sw = Stopwatch.StartNew(); Console.WriteLine("Before Wait"); task.Wait(); Console.WriteLine("After Wait: {0}ms", sw.ElapsedMilliseconds); Console.WriteLine("Result: {0}, Elapsed={1}ms", task.Result, sw.ElapsedMilliseconds);输出:
Before Wait After Wait: 1002ms Result: Hello World, Elapsed=1002ms可以看到,task.Wait 阻塞了当前线程,直到 task 完成。
2.task.GetAwaiter().GetResult()
public class Task<TResult> : Task { // 堆代码 duidaima.com } public class Task { // 1. 无参数,无返回值,阻塞当前线程至 task 完成 public void Wait() { Wait(Timeout.Infinite, default); } // 2. 无参数,有返回值,阻塞当前线程至 task 完成或 超时 // 如果超时后 task 仍未完成,返回 False,否则返回 True public bool Wait(TimeSpan timeout) { return Wait((int)timeout.TotalMilliseconds, default); } // 3. 和 2 一样,只是参数类型不同 public bool Wait(int millisecondsTimeout) { return Wait(millisecondsTimeout, default); } // 4. 无参数,无返回值,阻塞当前线程至 task 完成或 cancellationToken 被取消 // cancellationToken 被取消时抛出 OperationCanceledException public void Wait(CancellationToken cancellationToken) { Wait(Timeout.Infinite, cancellationToken); } // 5. 无参数,有返回值,阻塞当前线程至 task 完成或 超时 或 cancellationToken 被取消 // 如果超时后 task 仍未完成,返回 False,否则返回 True // cancellationToken 被取消时抛出 OperationCanceledException public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfContinuationIsNotNull(); return InternalWaitCore(millisecondsTimeout, cancellationToken); } }下面是一个使用 bool Wait(int millisecondsTimeout) 的例子:
var task = Task.Run(() => { Thread.Sleep(1000); return "Hello World"; }); var sw = Stopwatch.StartNew(); Console.WriteLine("Before Wait"); bool completed = task.Wait(millisecondsTimeout: 200); Console.WriteLine("After Wait: completed={0}, Elapsed={1}", completed, sw.ElapsedMilliseconds); Console.WriteLine("Result: {0}, Elapsed={1}", task.Result, sw.ElapsedMilliseconds);输出:
Before Wait After Wait: completed=False, Elapsed=230 Result: Hello World, Elapsed=1001因为指定的 millisecondsTimeout 不足以等待 task 完成,所以 task.Wait 返回 False,继续执行后续代码。但是,task.Result 仍然会阻塞当前线程,直到 task 完成。关联的方法还有 Task.WaitAll 和 Task.WaitAny。同样也是非必要情况下,不建议使用。
Task<string> RunTask() { return Task.Run(() => { Thread.Sleep(1000); return "Hello World!"; }); } var task1 = RunTask(); task1.Wait(); var task2 = RunTask(); task2.GetAwaiter().GetResult(); var task3 = RunTask(); _ = task3.Result;
public class Task { internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken) => InternalWaitCore(millisecondsTimeout, cancellationToken); private bool InternalWaitCore(int millisecondsTimeout, CancellationToken cancellationToken) { // 如果 Task 已经完成,直接返回 true bool returnValue = IsCompleted; if (returnValue) { return true; } // 如果调用的是 Task.Wait 的无参重载方法,且Task 已经完成或者在内联执行后完成,直接返回 true,不会阻塞 Task.Wait 的调用线程。 // WrappedTryRunInline 的意思是尝试在捕获的 TaskScheduler 中以内联的方式执行 Task,此处不展开 if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled && WrappedTryRunInline() && IsCompleted) { returnValue = true; } else { // Task 未完成,调用 SpinThenBlockingWait 方法,阻塞当前线程,直到 Task 完成或超时或 cancellationToken 被取消 returnValue = SpinThenBlockingWait(millisecondsTimeout, cancellationToken); } return returnValue; } private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken) { bool infiniteWait = millisecondsTimeout == Timeout.Infinite; uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount; bool returnValue = SpinWait(millisecondsTimeout); if (!returnValue) { var mres = new SetOnInvokeMres(); try { // 将 mres 作为 Task 的 Continuation,当 Task 完成时,会调用 mres.Set() 方法 AddCompletionAction(mres, addBeforeOthers: true); if (infiniteWait) { bool notifyWhenUnblocked = ThreadPool.NotifyThreadBlocked(); try { // 没有指定超时时间,阻塞当前线程,直到 Task 完成或 cancellationToken 被取消 returnValue = mres.Wait(Timeout.Infinite, cancellationToken); } finally { if (notifyWhenUnblocked) { ThreadPool.NotifyThreadUnblocked(); } } } else { uint elapsedTimeTicks = ((uint)Environment.TickCount) - startTimeTicks; if (elapsedTimeTicks < millisecondsTimeout) { bool notifyWhenUnblocked = ThreadPool.NotifyThreadBlocked(); try { // 指定了超时时间,阻塞当前线程,直到 Task 完成或 超时 或 cancellationToken 被取消 returnValue = mres.Wait((int)(millisecondsTimeout - elapsedTimeTicks), cancellationToken); } finally { if (notifyWhenUnblocked) { ThreadPool.NotifyThreadUnblocked(); } } } } } finally { // 如果因为超时或 cancellationToken 被取消,而导致 Task 未完成,需要将 mres 从 Task 的 Continuation 中移除 if (!IsCompleted) RemoveContinuation(mres); } } return returnValue; } private bool SpinWait(int millisecondsTimeout) { if (IsCompleted) return true; if (millisecondsTimeout == 0) { // 如果指定了超时时间为 0,直接返回 false return false; } // 自旋至少一次,总次数由 Threading.SpinWait.SpinCountforSpinBeforeWait 决定 // 如果 Task 在自旋期间完成,返回 true int spinCount = Threading.SpinWait.SpinCountforSpinBeforeWait; SpinWait spinner = default; while (spinner.Count < spinCount) { // -1 表示自旋期间不休眠,不会让出 CPU 时间片 spinner.SpinOnce(sleep1Threshold: -1); if (IsCompleted) { return true; } } // 自旋结束后,如果 Task 仍然未完成,返回 false return false; } private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction { // 往父类 ManualResetEventSlim 中传入 false,表示 ManualResetEventSlim 的初始状态为 nonsignaled // 也就是说,在调用 ManualResetEventSlim.Set() 方法之前,ManualResetEventSlim.Wait() 方法会阻塞当前线程 internal SetOnInvokeMres() : base(false, 0) { } public void Invoke(Task completingTask) { Set(); } public bool InvokeMayRunArbitraryCode => false; } }Task.Wait 的两个阶段