web-dev-qa-db-ja.com

非同期メソッドをスレッドまたはタスクとして開始する

私はC#s await/asyncと現在少し遊んでいます。

私のシナリオでは、WebRequestプロパティを持つ単純なクライアントオブジェクトがあります。クライアントは、WebRequests RequestStreamを介してaliveメッセージを定期的に送信する必要があります。これはクライアントオブジェクトのコンストラクタです:

public Client()
{
    _webRequest = WebRequest.Create("some url");
    _webRequest.Method = "POST";

    IsRunning = true;

    // --> how to start the 'async' method (see below)
}

および非同期のalive-senderメソッド

private async void SendAliveMessageAsync()
{
    const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
    var seconds = 0;
    while (IsRunning)
    {
        if (seconds % 10 == 0)
        {
            await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
        }

        await Task.Delay(1000);
        seconds++;
    }
}

メソッドはどのように開始すべきですか?

新しいThread(SendAliveMessageAsync).Start();

または

Task.Run(SendAliveMessageAsync); //戻り値の型をTaskに変更します

または

sendAliveMessageAsync();を待ちます。 //コンストラクタが非同期でないため失敗します

私の質問は、await/asyncいくつかの点で間違っていると思います。

3番目のオプションは投げています

The 'await' operator can only be used in a method or lambda marked with the 'async' modifier
8
KingKerosin

メソッドはどのように開始すべきですか?

「上記のどれにも」投票します。 :)

「Fire and Forget」は、正しく処理するのが難しいシナリオです。特に、エラー処理には常に問題があります。この場合、 async voidびっくりするかもしれません。

すぐにタスクをawaitingしない場合は、タスクを明示的に保存することをお勧めします。

private async Task SendAliveMessageAsync();

public Task KeepaliveTask { get; private set; }

public Client()
{
  ...
  KeepaliveTask = SendAliveMessageAsync();
}

これにより、少なくともClientのコンシューマはSendAliveMessageAsyncメソッドによってスローされた例外を検出して回復できます。

余談ですが、このパターンはmy "非同期初期化"パターン とほぼ同じです。

7
Stephen Cleary

以前の答えが間違っていたとして編集されました:

それはコンストラクター内にあるので、新しいスレッドを作成する必要があると思います。私は個人的にそれを使ってやります

Task.Factory.StartNew(()=> SendAliveMessageAsync());

4
John Clifford

fire and forget操作なので、次のコマンドを使用して開始する必要があります

SendAliveMessageAsync();

awaitは新しいTaskを開始しないことに注意してください。 Taskが完了するのを待つのは、単なる構文上の砂糖です。
Task.Runを使用して新しいスレッドが開始されます。

したがって、SendAliveMessageAsync内で新しいタスクを開始する必要があります。

private async Task SendAliveMessageAsync()
{
    const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
    await Task.Run( () => {
        var seconds = 0;
        while (IsRunning)
        {
            if (seconds % 10 == 0)
            {
                await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage);
            }

            await Task.Delay(1000);
            seconds++;
        }
    });
}
0
Domysee

コンストラクター内からawaitを呼び出すことができないため、ここにオプションがあります。

MicrosoftのReactive Framework(NuGet "Rx-Main")の使用をお勧めします。

コードは次のようになります。

_public class Client
{
    System.Net.WebRequest _webRequest = null;
    IDisposable _subscription = null;

    public Client()
    {
        _webRequest = System.Net.WebRequest.Create("some url");
        _webRequest.Method = "POST";

        const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";

        var keepAlives =
            from n in Observable.Interval(TimeSpan.FromSeconds(10.0))
            from u in Observable.Using(
                () => new StreamWriter(_webRequest.GetRequestStream()),
                sw => Observable.FromAsync(() => sw.WriteLineAsync(keepAliveMessage)))
            select u;

        _subscription = keepAlives.Subscribe();
    }
}
_

このコードは、必要なすべてのスレッドを処理し、StreamWriterを適切に破棄します。

キープアライブを停止したいときはいつでも_subscription.Dispose()を呼び出すだけです。

0
Enigmativity

コードでasync/awaitを使用する必要はありません。新しいスレッドをセットアップして長い操作を実行するだけです。

_private void SendAliveMessage()
{
    const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
    var sreamWriter = new StreamWriter(_webRequest.GetRequestStream());
    while (IsRunning)
    {
        sreamWriter.WriteLine(keepAliveMessage);
        Thread.Sleep(10 * 1000);
    }    
}
_

Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning)を使用して操作を実行します。

本当にasync/awaitパターンを使いたい場合は、await修飾子なしでコンストラクターでそれを呼び出し、それを忘れてください。

_public Client()
{
    _webRequest = WebRequest.Create("some url");
    _webRequest.Method = "POST";

    IsRunning = true;

    SendAliveMessageAsync();    //just call it and forget it.
}
_

実行時間の長いスレッドを設定したり、非同期/待機パターンを使用したりすることはお勧めしません。この状況では、タイマーの方が適している可能性があります。

0
cFish