6

ActionBlockを持つクラスReceiverがあります。

public class Receiver<T> : IReceiver<T>
{

  private ActionBlock<T> _receiver;

  public Task<bool> Send(T item) 
  {
     if(_receiver!=null)
        return _receiver.SendAsync(item);

     //Do some other stuff her
  }

  public void Register (Func<T, Task> receiver)
  {
    _receiver = new ActionBlock<T> (receiver);
  }

  //...
}

ActionBlockのRegister-Actionは、await-Statementを持つ非同期メソッドです。

private static async Task Writer(int num)
{
   Console.WriteLine("start " + num);
   await Task.Delay(500);
   Console.WriteLine("end " + num);
}

今私がやりたいことは、排他的な振る舞いを得るためにアクションメソッドが終了するまで(条件が設定されている場合)同期的に待つことです。

var receiver = new Receiver<int>();
receiver.Register((Func<int, Task) Writer);
receiver.Send(5).Wait(); //does not wait the action-await here!

問題は「Task.Delay(500)を待っているとき」です。ステートメントが実行され、 "receiver.Post(5).Wait();"もう待つことはありません。

いくつかの変種(TaskCompletionSource、ContinueWith、...)を試してみましたが、うまくいきません。

誰かが問題を解決するためのアイデアを持っていますか?


  • を変更してコードを変更できませんでした_receiverTransformBlockそして次の行動を新しいActionBlock、 にリンク_receiver? - svick
  • 小さなコードの例を教えてください。そのリファクタリングがどのようにして"排他的動作"によって解決されるのか理解できません。問題。 - obi111

1 답변


3

ActionBlockデフォルトでは排他的な振る舞いを強制します(一度に1つの項目だけが処理されます)。あなたが「排他的な振る舞い」によって何か他のことを意味するならば、あなたは使うことができますTaskCompletionSourceアクションが完了したときに送信者に通知するには

... use ActionBlock<Tuple<int, TaskCompletionSource<object>>> and Receiver<Tuple<int, TaskCompletionSource<object>>>
var receiver = new Receiver<Tuple<int, TaskCompletionSource<object>>>();
receiver.Register((Func<Tuple<int, TaskCompletionSource<object>>, Task) Writer);
var tcs = new TaskCompletionSource<object>();
receiver.Send(Tuple.Create(5, tcs));
tcs.Task.Wait(); // if you must

private static async Task Writer(int num, TaskCompletionSource<object> tcs)
{
  Console.WriteLine("start " + num);
  await Task.Delay(500);
  Console.WriteLine("end " + num);
  tcs.SetResult(null);
}

代わりに、あなたは使用することができますAsyncLock私のAsyncExライブラリに含まれています):

private static AsyncLock mutex = new AsyncLock();

private static async Task Writer(int num)
{
  using (await mutex.LockAsync())
  {
    Console.WriteLine("start " + num);
    await Task.Delay(500);
    Console.WriteLine("end " + num);
  }
}


  • はい、ActionBlockは排他的な振る舞いを強制しますが、登録されたアクションが非同期である場合は「本当の排他的」ではありません。もうこれ以上。はい、あなたの解決策はうまくいくはずですが、TaskCompletionSourceパラメータを追加する必要はありません。なぜなら、そのアクションは排他的ロジックのエントリポイントだからです。つまり、ユーザがtcs.SetResultを呼び出さないと、もううまくいきません... - obi111
  • その場合は、あなたが使用することができますAsyncLock。コードサンプルについては、最新の回答を参照してください。アイテムがいつ処理されたかはもうわかりませんが、各アイテムは一度に1つずつ処理されます(以下を含む)。async処理)。 - Stephen Cleary
  • オーケーありがとうございます私はそれがまさに私が必要とするものであると思います - 私はそれを試みます! - obi111

関連する質問

最近の質問