2012-02-06 15 views
17

Cách được khuyến nghị để thêm postconditions vào các phương thức không đồng bộ trả về Task<T> là gì?Hợp đồng mã và không đồng bộ

Tôi đã đọc các gợi ý sau:

http://social.msdn.microsoft.com/Forums/hu-HU/async/thread/52fc521c-473e-4bb2-a666-6c97a4dd3a39

Các bài đề nghị thực hiện mỗi phương pháp là đồng bộ, hợp đồng đó, và sau đó thực hiện một đối tác async như là một wrapper đơn giản. Rất tiếc, tôi không thấy giải pháp này là giải pháp khả thi (có thể thông qua sự hiểu lầm của riêng tôi):

  1. Phương pháp async mặc dù không có bất kỳ hợp đồng mã thực nào và do đó có thể làm theo ý muốn.
  2. Codebases được cam kết không đồng bộ có khả năng không triển khai đối tác đồng bộ hóa cho mọi thứ. Kết quả là, việc triển khai các phương thức mới chứa await s trên các phương thức async khác do đó buộc phải không đồng bộ. Những phương thức này thực chất không đồng bộ và không thể dễ dàng được chuyển đổi thành đồng bộ. Chúng không chỉ đơn giản là hàm bao.

Ngay cả khi chúng tôi không còn giá trị điểm sau bằng cách nói chúng ta có thể sử dụng .Result hoặc .Wait() thay vì await (mà thực sự sẽ gây ra một số SyncContext s đến bế tắc, và sẽ phải được viết lại trong phương pháp async anyway), Tôi vẫn còn bị thuyết phục về điểm đầu tiên.

Có bất kỳ ý tưởng thay thế nào hoặc có bất kỳ điều gì mà tôi đã bỏ lỡ về hợp đồng mã và TPL không?

+1

Không ai nói MVP không thể có được nó sai. –

Trả lời

14

Tôi đã chỉ ra điều này cho nhóm Async, như những người khác đã làm. Hiện tại, Hợp đồng và Async là (hầu như) loại trừ lẫn nhau. Vì vậy, ít nhất một số người trong Microsoft nhận thức được vấn đề, nhưng tôi không biết họ đang định làm gì với nó.

Tôi không khuyên bạn nên viết các phương thức không đồng bộ làm trình bao bọc cho các phương thức đồng bộ hóa. Trong thực tế, tôi sẽ có xu hướng làm ngược lại.

Điều kiện tiên quyết có thể hoạt động. Gần đây tôi chưa thử; bạn có thể cần một trình bao bọc nhỏ xung quanh phương thức async của bạn bao gồm các điều kiện tiên quyết.

Điều kiện bị hỏng khá nhiều.

Các xác nhận và giả định hoạt động bình thường, nhưng trình kiểm tra tĩnh thực sự bị hạn chế vì các điều kiện sau bị hỏng.

Các biến thể không có ý nghĩa nhiều trong thế giới Async, nơi trạng thái có thể thay đổi có xu hướng chỉ cản trở. (Async nhẹ nhàng đẩy bạn ra khỏi OOP và hướng tới một phong cách chức năng).

Hy vọng trong VS vNext, Hợp đồng sẽ được cập nhật với loại điều kiện không đồng ý, điều này cũng cho phép trình kiểm tra tĩnh hoạt động tốt hơn với các xác nhận trong các phương thức không đồng bộ.

Trong khi đó, bạn có thể có một giả vờ-hậu bằng cách viết một giả định:

// Synchronous version for comparison. 
public static string Reverse(string s) 
{ 
    Contract.Requires(s != null); 
    Contract.Ensures(Contract.Result<string>() != null); 

    return ...; 
} 

// First wrapper takes care of preconditions (synchronously). 
public static Task<string> ReverseAsync(string s) 
{ 
    Contract.Requires(s != null); 

    return ReverseWithPostconditionAsync(s); 
} 

// Second wrapper takes care of postconditions (asynchronously). 
private static async Task<string> ReverseWithPostconditionAsync(string s) 
{ 
    var result = await ReverseImplAsync(s); 

    // Check our "postcondition" 
    Contract.Assume(result != null); 

    return result; 
} 

private static async Task<string> ReverseImplAsync(string s) 
{ 
    return ...; 
} 

Một số tập quán của đồng mã chỉ là không thể - ví dụ, quy định cụ thể postconditions về thành viên async của giao diện hoặc các lớp học cơ sở .

Cá nhân, tôi vừa tránh các hợp đồng hoàn toàn trong mã Async của tôi, hy vọng rằng Microsoft sẽ sửa chữa nó trong một vài tháng.

+0

Bạn đã đề cập rằng bạn hy vọng "rằng Microsoft sẽ sửa chữa nó trong một vài tháng" Tình hình có thay đổi từ khi bạn đăng bài này không? Bạn vẫn còn tránh các hợp đồng trên các phương thức không đồng bộ? – julealgon

+2

@julealgon: Thật không may, không. Tôi vẫn tránh các hợp đồng về các phương thức không đồng bộ. Và tôi vẫn hy vọng rằng MS sẽ sửa lỗi này. :) –

+0

Tình hình đã thay đổi kể từ đó. Hãy xem câu trả lời của tôi bên dưới. –

2

Nhập mã này nhưng quên nhấn "Đăng" ... :)

Hiện tại không có hỗ trợ chuyên môn cho việc này. Điều tốt nhất bạn có thể làm là một cái gì đó như thế này (không sử dụng async từ khóa, nhưng ý tưởng tương tự - đó là khả năng các Ổ ghi sẽ hoạt động khác dưới CTP async, tôi đã không thử nó chưa):

public static Task<int> Do() 
{ 
    Contract.Ensures(Contract.Result<Task<int>>() != null); 
    Contract.Ensures(Contract.Result<Task<int>>().Result > 0); 

    return Task.Factory.StartNew(() => { Thread.Sleep(3000); return 2; }); 
} 

public static void Main(string[] args) 
{ 
    var x = Do(); 
    Console.WriteLine("processing"); 
    Console.WriteLine(x.Result); 
} 

Tuy nhiên, điều này có nghĩa là phương pháp 'không đồng bộ' sẽ không thực sự trở lại cho đến khi Tác vụ hoàn thành việc đánh giá, do đó, "xử lý" sẽ không được in cho đến khi 3 giây trôi qua. Điều này tương tự như vấn đề với các phương thức trả lại một cách lười biếng IEnumerable s — Hợp đồng phải liệt kê tất cả các mục trong IEnumerable để đảm bảo rằng điều kiện giữ, ngay cả khi người gọi sẽ không thực sự sử dụng tất cả các mục.

Bạn có thể giải quyết vấn đề này bằng cách thay đổi chế độ hợp đồng thành Preconditions, nhưng điều này có nghĩa là không có điều kiện nào thực sự được kiểm tra.

Trình kiểm tra tĩnh cũng không thể kết nối Result với lambda, vì vậy bạn sẽ nhận được thông báo "Đảm bảo chưa được chứng minh". (Nói chung, kiểm tra tĩnh không chứng minh mọi thứ về lambdas/delegate.)

Tôi nghĩ rằng để nhận được sự hỗ trợ thích hợp cho Nhiệm vụ/chờ đợi, nhóm Hợp đồng Code sẽ phải thực hiện các nhiệm vụ đặc biệt để chỉ thêm kiểm tra điều kiện tiên quyết khi truy cập vào trường Result.

+0

Cảm ơn bạn đã cung cấp thông tin - Tôi thậm chí không nghĩ đến các bộ sưu tập lười biếng: -/ –

+0

Vâng, bạn có thể bật công tắc (bỏ qua số lượng) sẽ bỏ qua các hợp đồng 'Contract.ForAll' để tránh gặp vấn đề với chúng. Không có chuyển đổi nào cho Công việc (chưa). – porges

0

gửi bài trả lời mới cho chủ đề cũ này vì nó sẽ được trả về bởi google là câu trả lời đầu tiên đặt câu hỏi về CodeContract và Async

curently Hợp đồng về phương pháp async trở về công tác đang làm việc một cách chính xác, và không có nhu cầu để tránh chúng .

hợp đồng Standart cho phương pháp async:

[ContractClass(typeof(ContractClassForIFoo))] 
public interface IFoo 
{ 
    Task<object> MethodAsync(); 
} 


[ContractClassFor(typeof(IFoo))] 
internal abstract class ContractClassForIFoo : IFoo 
{ 
    #region Implementation of IFoo 

    public Task<object> MethodAsync() 
    { 
     Contract.Ensures(Contract.Result<Task<object>>() != null); 
     Contract.Ensures(Contract.Result<Task<object>>().Status != TaskStatus.Created); 
     Contract.Ensures(Contract.Result<object>() != null); 
     throw new NotImplementedException(); 
    } 

    #endregion 
} 

public class Foo : IFoo 
{ 
    public async Task<object> MethodAsync() 
    { 
     var result = await Task.FromResult(new object()); 
     return result; 
    } 
} 

Nếu bạn nghĩ rằng hợp đồng mà không nhìn đúng tôi đồng ý có vẻ sai lệch ít nhất, nhưng nó làm việc. Và nó không giống như các hợp đồng viết lại lực lượng đánh giá nhiệm vụ sớm.

Khi Stephen nêu ra một số nghi ngờ đã thực hiện thêm một số thử nghiệm và hợp đồng trong trường hợp của tôi đã làm điều đúng đắn.

Mã dùng để thử nghiệm:

public static class ContractsAbbreviators 
{ 
    [ContractAbbreviator] 
    public static void EnsureTaskIsStarted() 
    { 
     Contract.Ensures(Contract.Result<Task>() != null); 
     Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created); 
    } 

} 

[ContractClass(typeof(ContractClassForIFoo))] 
public interface IFoo 
{ 
    Task<int> MethodAsync(int val); 
} 

[ContractClassFor(typeof(IFoo))] 
internal abstract class ContractClassForIFoo : IFoo 
{ 
    public Task<int> MethodAsync(int val) 
    { 
     Contract.Requires(val >= 0); 
     ContractsAbbreviators.EnsureTaskIsStarted(); 
     Contract.Ensures(Contract.Result<int>() == val); 
     Contract.Ensures(Contract.Result<int>() >= 5); 
     Contract.Ensures(Contract.Result<int>() < 10); 
     throw new NotImplementedException(); 
    } 
} 

public class FooContractFailTask : IFoo 
{ 
    public Task<int> MethodAsync(int val) 
    { 
     return new Task<int>(() => val); 
     // esnure raises exception // Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created); 
    } 
} 

public class FooContractFailTaskResult : IFoo 
{ 
    public async Task<int> MethodAsync(int val) 
    { 
     await Task.Delay(val).ConfigureAwait(false); 
     return val + 1; 
     // esnure raises exception // Contract.Ensures(Contract.Result<int>() == val); 
    } 
} 

public class Foo : IFoo 
{ 
    public async Task<int> MethodAsync(int val) 
    { 
     const int maxDeapth = 9; 

     await Task.Delay(val).ConfigureAwait(false); 

     if (val < maxDeapth) 
     { 
      await MethodAsync(val + 1).ConfigureAwait(false); 
     } 

     return val; 
    } 
} 
+0

Nhưng bạn không thể diễn tả các hợp đồng như "số nguyên sẽ nằm trong phạm vi [5, 10)", và tôi tin rằng các điều kiện tiên quyết được thể hiện trong cơ thể thực hiện cũng không hoạt động như mong đợi. –

+0

Điều đó không hiệu quả đối với tôi. Nếu tôi có một phương thức async trả về 'Task ' và tôi viết 'Contract.Ensures (Contract.Result ()! = Null)' ngay từ đầu, nó gây ra một 'BadImageFormatException'. – piedar