2013-08-30 21 views
12

Delay(0) luôn luôn được gạch chân không? Theo kinh nghiệm của tôi, có nghĩa là:Task.Yield() so với Task.Delay (0)

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication 
{ 
    class Program 
    { 
     static async Task Test() 
     { 
      await Task.Yield(); 
      Console.WriteLine("after Yield(), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      await Task.Delay(0); 
      Console.WriteLine("after Delay(0), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      await Task.Delay(100); 
      Console.WriteLine("after Delay(100), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
     } 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      Test().Wait(); 
     } 
    } 
} 

Đây là ứng dụng giao diện điều khiển, do đó, nhóm chủ đề được sử dụng để tiếp tục. Kết quả:

Main thread: 11 
after Yield(), thread: 7 
after Delay(0), thread: 7 
after Delay(100), thread: 6 

Trả lời

21

Bên Task.Delay, nó trông giống như thế này (single phiên bản thông số (int) chỉ gọi là phiên bản dưới đây):

[__DynamicallyInvokable] 
public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken) 
{ 
    if (millisecondsDelay < -1) 
    { 
     throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay")); 
    } 
    if (cancellationToken.IsCancellationRequested) 
    { 
     return FromCancellation(cancellationToken); 
    } 
    if (millisecondsDelay == 0) 
    { 
     return CompletedTask; 
    } 
    DelayPromise state = new DelayPromise(cancellationToken); 
    if (cancellationToken.CanBeCanceled) 
    { 
     state.Registration = cancellationToken.InternalRegisterWithoutEC(delegate (object state) { 
      ((DelayPromise) state).Complete(); 
     }, state); 
    } 
    if (millisecondsDelay != -1) 
    { 
     state.Timer = new Timer(delegate (object state) { 
      ((DelayPromise) state).Complete(); 
     }, state, millisecondsDelay, -1); 
     state.Timer.KeepRootedWhileScheduled(); 
    } 
    return state; 
} 

Như bạn hy vọng có thể thấy:

if (millisecondsDelay == 0) 
    { 
     return CompletedTask; 
    } 

Điều này có nghĩa là nó luôn trả về một tác vụ đã hoàn thành và do đó mã của bạn sẽ luôn tiếp tục chạy qua dòng await cụ thể đó.

8

Vâng, đúng vậy. Một kiểm tra của IL trong hiển thị phản xạ (trong số các logic khác):

if (millisecondsDelay == 0) 
{ 
    return CompletedTask; 
} 

Vì vậy, có, nó sẽ đưa bạn trở lại một tác vụ đã hoàn thành trong trường hợp này.

Lưu ý rằng việc triển khai await bao gồm các kiểm tra đảm bảo rằng tác vụ đã hoàn thành không gây ra chuyển đổi ngữ cảnh bổ sung, vì vậy có: mã của bạn sẽ tiếp tục chạy mà không cần tạm dừng ở đây.

Trả lại tác vụ đã hoàn thành là mẹo được khuyến nghị khi câu trả lời đã được biết/có sẵn đồng bộ; nó cũng phổ biến với bộ nhớ cache Task s cho các giá trị kết quả chung.

+0

Tuyệt vời, cảm ơn! Trông với tôi như một cách thuận tiện để tạo một nhiệm vụ không chung chung [trong trạng thái hoàn thành] (http://stackoverflow.com/a/18527377/1768303). Chấp nhận câu trả lời của Damien về mặt kỹ thuật anh ta là người đầu tiên:] – Noseratio

+0

@Noseratio Tôi nghĩ rằng sử dụng một cái gì đó như 'Task complete = Task.FromResult (true);' là tốt hơn, bởi vì nó được đảm bảo để làm việc. Tôi nghĩ rằng 'Task.Delay (0)' không bắt buộc phải trả về một 'Task' đã hoàn thành. – svick

+0

@svick, tôi đồng ý 'Task.FromResult (true)' thích hợp hơn, nhưng tôi vẫn thích 'Task.Delay (millisecondsDelay: 0)' vì tôi có thể dễ dàng mô phỏng cả việc đồng bộ và tiếp tục async chỉ bằng cách thay đổi 'millisecondsDelay'. Bạn có nghĩ rằng họ có thể thay đổi hành vi này? Điều đó trông giống như một sự thay đổi phá vỡ đối với tôi, đưa ra đoạn mã trên. – Noseratio

Các vấn đề liên quan