12

私はネットワーク化されたアプリケーションを書いています。

メッセージは、次のようにトランスポートを介して送信されます。

Network.SendMessage (new FirstMessage() );

次のように、このメッセージタイプが到着したときに呼び出されるイベントハンドラーを登録できます。

Network.RegisterMessageHandler<FirstMessage> (OnFirstMessageReceived);

そして、イベントが発生します:

public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e)
{
}

ネットワークアプリケーションのカスタム認証手順を作成しています。これを完了するには、約5つのメッセージが必要です。

タスク並列ライブラリを使用しないと、次のように、前のイベントハンドラーで各プロシージャの次のステップをコーディングする必要があります。

public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e)
{
     Network.SendMessage( new SecondMessage() );
}

public void OnSecondMessageReceived(EventArgs<SecondMessageEventArgs> e)
{
     Network.SendMessage( new ThirdMessage() );
}

public void OnThirdMessageReceived(EventArgs<ThirdMessageEventArgs> e)
{
     Network.SendMessage( new FourthMessage() );
}

public void OnFourthMessageReceived(EventArgs<FourthMessageEventArgs> e)
{
     // Authentication is complete
}

ソースコードを飛び回って、この一部と一部をコーディングするというアイデアは好きではありません。理解して編集するのは難しいです。

タスク並列ライブラリは、このソリューションを大幅に簡素化すると聞いています。

ただし、タスク並列ライブラリを使用して読んだ例の多くは、アクティブなタスクのチェーンの開始に関連していました。「アクティブ」とは、次のように、明示的に呼び出されたときに各タスクを開始できることを意味します。

public void Drink() {}
public void Eat()   {}
public void Sleep() {}

Task.Factory.StartNew(     () => Drink() )
            .ContinueWith( () => Eat()   )
            .ContinueWith( () => Sleep() );

これは、メッセージが受信されたときにのみ各イベントハンドラーメソッドが呼び出されるイベントベースの非同期パターンとは逆です。

言い換えれば、私はこのようなことをすることはできません(しかし私はしたいです):

Task.Factory.StartNew(     () => OnFirstMessageReceived()  )
            .ContinueWith( () => OnSecondMessageReceived() )
            .ContinueWith( () => OnThirdMessageReceived()  )
            .ContinueWith( () => OnFourthMessageReceived() );

この記事を読みましたが、よくわかりません。私が必要としているのはと関係があるようですTaskCompletionSource。上記のコードブロックのようなイベントベースの非同期パターンからタスクを作成したい場合、どのようになりますか?

4

2 に答える 2

21

TaskCompletionSourceについては正しいです。これは、EAP(イベントベースの非同期パターン)をTPLのタスクに変換するための鍵です。

これはここに文書化されています:https ://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/tpl-and-traditional-async-programming#exposed-complex-eap-operations-as-tasks

簡略化されたコードは次のとおりです。

public static class Extensions  
{
    public static Task<XDocument> GetRssDownloadTask(
        this WebClient client, Uri rssFeedUri)
    {
        // task completion source is an object, which has some state.
        // it gives out the task, which completes, when state turns "completed"
        // or else it could be canceled or throw an exception
        var tcs = new TaskCompletionSource<XDocument>(); 

        // now we subscribe to completed event. depending on event result
        // we set TaskCompletionSource state completed, canceled, or error
        client.DownloadStringCompleted += (sender, e) => 
        {
                  if(e.Cancelled) 
                  {
                      tcs.SetCanceled();
                  }
                  else if(null != e.Error)
                  {
                      tcs.SetException(e.Error);
                  }
                  else
                  { 
                      tcs.SetResult(XDocument.Parse(e.Result));
                  }
        };

        // now we start asyncronous operation
        client.DownloadStringAsync(rssFeedUri);
        // and return the underlying task immediately
        return tcs.Task;
    }
}

さて、これらの操作のチェーンを作成するために必要なのは、継続を設定することだけです(これは現時点ではあまり快適ではなく、C#5が待機し、非同期がそれを大いに助けます)

したがって、このコードは次のように使用できます。

public static void Main()
{
    var client = new WebClient();

    client.GetRssDownloadTask(
        new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"))
        .ContinueWith( t => {
            ShowXmlInMyUI(t.Result); // show first result somewhere
            // start a new task here if you want a chain sequence
        });

    // or start it here if you want to get some rss feeds simultaneously

    // if we had await now, we would add 
    // async keyword to Main method defenition and then

    XDocument feedEric = await client.GetRssDownloadTask(
        new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"));
    XDocument feedJon = await client.GetRssDownloadTask(
        new Uri("http://feeds.feedburner.com/JonSkeetCodingBlog?format=xml"));
    // it's chaining - one task starts executing after 
    // another, but it is still asynchronous
}
于 2011-09-08T15:23:24.733 に答える
3

Jeremy Liknessのブログエントリのタイトルは、Reactive Extensions(Rx)を使用した非同期シーケンシャルワークフローのコルーチンです。彼が答えようとしている質問は次のとおりです。

概念は単純です。非同期の一連の操作を順番に実行したい場合がよくあります。おそらく、サービスからリストをロードしてから、選択したアイテムをロードしてから、アニメーションをトリガーする必要があります。これは、完了したイベントをチェーンするか、ラムダ式をネストすることで実行できますが、よりクリーンな方法はありますか?

于 2011-09-08T01:33:10.520 に答える