17

戻る非同期メソッドに事後条件を追加するための推奨される方法は何Task<T>ですか?

私は次の提案を読みました:

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

この投稿では、各メソッドを同期として実装し、それを縮小してから、非同期の対応するメソッドを単純なラッパーとして実装することを提案しています。残念ながら、私はこれを実行可能な解決策とは考えていません(おそらく私自身の誤解による):

  1. asyncメソッドは、syncメソッドのラッパーであると想定されていますが、実際のコードコントラクトがないままであるため、必要に応じて実行できます。
  2. 非同期にコミットしているコードベースは、すべてに対応する同期を実装する可能性は低いです。その結果、他の非同期メソッドにsを含む新しいメソッドを実装すると、await結果的に非同期になります。これらのメソッドは本質的に非同期であり、同期に簡単に変換することはできません。それらは単なるラッパーではありません。

.Result後者のポイントを使用できる、または.Wait()代わりに使用できると言って無効にしたとしてもawait(実際にはいくつかSyncContextのsがデッドロックになり、とにかくasyncメソッドで書き直す必要があります)、私はまだ最初のポイントについて確信しています。

別のアイデアはありますか、それともコード契約とTPLについて私が見逃したものはありますか?

4

3 に答える 3

14

他の人がしているように、私はこれを非同期チームに指摘しました。現在、ContractsとAsyncは(ほぼ)相互に排他的です。したがって、少なくともMicrosoftの一部の人々はこの問題を認識していますが、私は彼らがそれに対して何を計画しているのかを認識していません。

同期メソッドのラッパーとして非同期メソッドを作成することはお勧めしません。実際、私は反対のことをする傾向があります。

前提条件は機能します。最近は試していません。前提条件を含む非同期メソッドの周りに小さなラッパーが必要になる場合があります。

事後条件はかなり壊れています。

アサーションと仮定は正常に機能しますが、事後条件が破られているため、静的チェッカーは実際には制限されています。

不変条件は、可変状態が邪魔になる傾向がある非同期の世界ではあまり意味がありません。(非同期は、OOPから機能的なスタイルに向かって優しくプッシュします)。

うまくいけば、VS vNextでは、コントラクトが非同期対応の事後条件で更新されます。これにより、静的チェッカーが非同期メソッドのアサーションでより適切に機能するようになります。

それまでの間、assumeを書くことで、ふりをした後の状態にすることができます。

// 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 ...;
}

コードコントラクトの一部の使用法は不可能です。たとえば、インターフェイスまたは基本クラスの非同期メンバーに事後条件を指定します。

個人的には、Microsoftが数か月以内に修正することを期待して、非同期コードでコントラクトを完全に回避しました。

于 2012-02-07T01:53:10.377 に答える
2

これを入力しましたが、「投稿」を押すのを忘れました... :)

現在、これに対する特別なサポートはありません。あなたができる最善のことはこのようなものです(asyncキーワードを使用していませんが、同じ考えです-非同期CTPの下でリライターが異なる動作をする可能性があります、私はまだ試していません):

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);
}

ただし、これは、タスクが評価を終了するまで「async」メソッドが実際に戻らないことを意味します。したがって、「処理中」は3秒が経過するまで出力されません。これは、sを遅延的に返すメソッドの問題に似ています。呼び出し元が実際にすべてのアイテムを使用しない場合でもIEnumerable、コントラクトは条件が確実に成立するように、内のすべてのアイテムを列挙する必要があります。IEnumerable

コントラクトモードをに変更することでこれを回避できますPreconditionsが、これは事後条件が実際にチェックされないことを意味します。

静的チェッカーもResultラムダに接続できないため、「証明されていないことを確認します」というメッセージが表示されます。(一般に、静的チェッカーはラムダ/デリゲートについてのことを証明しません。)

Tasks / awaitの適切なサポートを得るには、コードコントラクトチームは、フィールドにアクセスしたときにのみ前提条件チェックを追加するために、特別な場合のタスクを実行する必要があると思いますResult

于 2012-02-07T11:01:39.500 に答える
0

CodeContractと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;
    }
}

契約が正しくないように見える場合は、少なくとも誤解を招くように見えることに同意しますが、機能します。そして、契約書の書き直しが時期尚早にタスクの評価を強制するようには見えません。

スティーブンがいくつかの疑問を提起したとき、私の場合はさらにいくつかのテストと契約が行われ、それらは正しく行われました。

テストに使用されるコード:

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;
    }
}
于 2016-11-21T10:14:43.447 に答える