2

IAsynchronousいくつかの操作(「開始/終了」パターン)を実行するための2つの方法をサポートするインターフェースがあると仮定します。

  • IAsyncResult BeginOperation(AsyncCallback callback, object state)
  • EndOperation(IAsyncResult ar)

また、私がクラスA : IAsynchronousとを持っていると仮定しB : IAsynchronousます。Compound : IAsynchronousクラスを実装する必要があります。のCompound操作は、の操作を呼び出しA、その完了を待ってから、Bの操作を呼び出してから、通常どおりコールバックを呼び出すことです。

Compound問題は、クラスをどのように設計するかです。

  • 元のオブジェクトを保持し、の操作が完了しstateたときに元のコールバックを呼び出す必要があります。したがって、単純にまたはCompoundに渡すことはできません。A.BeginOperationB.BeginOperation
  • また、またはCompound.EndOperationによってスローされた例外を再スローする必要があります。が例外をスローした場合、複合操作はすでに失敗しているため、 's操作は呼び出されません。A.EndOperationB.EndOperationA.EndOperationCompoundB.EndOperation
  • 内部操作が2つだけでなく、3つ以上ある場合はどうなりますか?一般的な方法はありますか?

明確にするために、次の例を考えてみましょう。Multiplier次のメソッドをサポートするクラスがあると仮定します。

  • IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, AsyncCallback callback, object state)
  • LargeNumber EndMultiply(IAsyncResult ar)

そしてThreeMultiplier、次のメソッドをサポートするクラスを作成します。

  • IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, LargeNumber z, AsyncCallback callback, object state)
  • LargeNumber EndMultiply(IAsyncResult ar)

クラスは、クラスをThreeMultiplier使用してMultiplierを計算する必要がありますx * y * z。これを行うために、最初にx * y(を介してMultiplier.Begin/EndMultiply)計算し、次に結果に。を掛けzます。もちろん、どのステップでも計算に失敗Multiplier.EndMultiplyするスローを投げることができます。SomeExceptionx * y * z

これを実装するための最良の(または良い)方法は何ですか?パターンはありますか?

4

2 に答える 2

4

新しいコードを書くときは、APM (非同期プログラミング モデル: IAsyncResult と Begin* と End* の使用) を避けます。

Visual Studio 2010 では、Task Asynchronous Pattern (TAP) が導入された時点で、Task Parallel Library (TPL) が導入されました。このパターンは、VS 2012 (C# 5) の新しい async/await キーワードをサポートする基礎となるフレームワーク API の基礎です。Task.FromAsync(); で APM 実装をラップできます。ただし、新しいコードを作成している場合は、将来的には Task/Task を使用する方が適切です。

TAP では、デリゲートを非同期的に実行する Task オブジェクトでデリゲートをラップします。その後、最初のデリゲートが完了したときに実行される他の非同期タスクを "続行" できます。たとえば、2 つのデリゲートがあり、一方が他方の完了時に実行される必要がある場合、次のようにすることができます。

        Task.Factory.StartNew(() => MyMethod())
            .ContinueWith(() => MyOtherMethod());

それを単一のメソッドでラップできます。

public void AsyncCompound(Action firstAcction, Action secondAction)
{
            Task.Factory.StartNew(firstAction)
                .ContinueWith(secondAction);
}

... IAsyncResult クラスを定義し、Begin メソッドと End メソッドの両方を実装する作業ははるかに少なくなります。

.NET 3.5 に関しては (一般的なパターンは APM です)、一貫して利用できる「タスク」クラスはありません。動作する可能性のある TPL 実装がいくつかありますが、私はそれらを使用していません。または、イベントベースではありますが、非同期操作を実装する別の方法であるため、Reactive Extensionsを調べることもできます。

APM は速く冗長になる可能性があるため、可能な限りデリゲートを使用することをお勧めします。http://msdn.microsoft.com/en-us/magazine/cc163467.aspxにあるような IAsyncResult 実装を再利用することもお勧めします。 例:

public class Multiplier
{
    public LargeNumber Multiply(LargeNumber x, LargeNumber y)
    {
        return x * y;
    }

    public IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, AsyncCallback callback, object state)
    {
        AsyncResult<LargeNumber> ar = new AsyncResult<BigInteger>(callback, state);
        ThreadPool.QueueUserWorkItem(o =>
        {
            var asyncResult = (AsyncResult<LargeNumber>)o;
            try
            {
                var largeNumber = Multiply(x, y);
                asyncResult.SetAsCompleted(largeNumber, false);
            }
            catch (Exception e)
            {
                asyncResult.SetAsCompleted(e, false);
            }
        }, ar);
        return ar;
    }

    public LargeNumber EndMultiply(IAsyncResult asyncResult)
    {
        var ar = (AsyncResult<LargeNumber>)asyncResult;

        return ar.EndInvoke();
    }

    public IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, LargeNumber z, AsyncCallback callback, object state)
    {
        AsyncResult<LargeNumber> ar = new AsyncResult<LargeNumber>(callback, state);

        BeginMultiply(x, y, (asyncResult1) =>
        {
            var firstResult = EndMultiply(asyncResult1);
            BeginMultiply(firstResult, z, (asyncResult2) =>
            {
                var secondResult = EndMultiply(asyncResult2);
                ar.SetAsCompleted(secondResult, true);
            }, state);
        }, state);
        return ar;
    }
}

次に、次のように使用して、値を非同期に計算し、現在のスレッドに戻ることができます。

var asyncResult = multiplier.BeginMultiply(x, y, z, ar => { }, null);
var result = multiplier.EndMultiply(asyncResult);

または、バックグラウンド スレッドで実行される他のコードにチェーンすることもできます。

        multiplier.BeginMultiply(x, y, z, ar =>
                                          {
                                            var result = multiplier.EndMultiply(ar);
                                            /* TODO: something with result on this background thread */
                                          }, null);

...必要な場所でその結果を取得する方法と、BeginMultiplyを呼び出したスレッドとどのように相互作用するかを決定するのはあなた次第です。

于 2012-06-23T04:00:11.273 に答える
1

非同期操作を組み合わせる他の優れた方法を実装したい場合は、Richter のSimplified APM With The AsyncEnumeratorと、で作成されたイテレータを使用してほぼ順次のコードを記述できる次の部分yieldを確認してください。

Wintellect サイトからライブラリをダウンロードします。

使用例 - AsyncEnumerator の操作

于 2012-06-23T04:47:06.130 に答える