簡體   English   中英

創建 ActionBlock 列表<t>這將在任何失敗時完成</t>

[英]Create list of ActionBlock<T> that will complete when any fail

在可以對“空”任務列表調用 await 的情況下。

我如何等待Task<T>列表,然后將新任務添加到等待列表,直到任務失敗或完成。

我確信這個問題必須有AwaiterCancellationTokenSource解決方案。

public class LinkerThingBob
{
    private List<Task> ofmyactions = new List<Task>();

    public void LinkTo<T>(BufferBlock<T> messages) where T : class
    {
        var action = new ActionBlock<IMsg>(_ => this.Tx(messages, _));

        // this would not actually work, because the WhenAny 
        // will not include subsequent actions.
        ofmyactions.Add(action.Completion);

        // link the new action block.
        this._inboundMessageBuffer.LinkTo(block);
    }

    // used to catch exceptions since these blocks typically don't end.
    public async Task CompletionAsync()
    {
        // how do i make the awaiting thread add a new action
        // to the list of waiting tasks without interrupting it
        // or graciously interrupting it to let it know there's one more

        // more importantly, this CompletionAsync might actually be called
        // before the first action is added to the list, so I actually need
        // WhenAny(INFINITE + ofmyactions)
        await Task.WhenAny(ofmyactions);
    }
}

我的問題是我需要一種機制,我可以將上面創建的每個action實例添加到Task<T>中,當出現異常時該實例將完成。

我不確定如何最好地解釋這一點,但是:

  • 在至少調用LinkTo<T>之前,任務必須完成,因此我需要從無限任務開始

  • 每次LinkTo<T>時,都必須將新操作添加到任務列表中,這些任務可能已經在另一個線程中等待。

沒有任何內置的東西,但使用TaskCompletionSource<T>構建一個並不難。 TCS 是您想要等待某事但尚無構造時使用的類型。 (自定義等待者用於更高級的場景)。

在這種情況下,像這樣的東西就足夠了:

public class LinkerThingBob
{
  private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();

  private async Task ObserveAsync(Task task)
  {
    try
    {
      await task;
      _tcs.TrySetResult(null);
    }
    catch (Exception ex)
    {
      _tcs.TrySetException(ex);
    }
  }

  public void LinkTo<T>(BufferBlock<T> messages) where T : class
  {
    var action = new ActionBlock<IMsg>(_ => this.Tx(messages, _));

    var _ = ObserveAsync(action.Completion);

    this._inboundMessageBuffer.LinkTo(block);
  }

  public Task Completion { get { return _tcs.Task; } }
}

Completion從未完成狀態開始。 可以使用ObserveAsync將任意數量的塊鏈接到它。 一旦其中一個塊完成, Completion也會完成。 我在這里編寫ObserveAsync的方式是,如果第一個完成的塊沒有錯誤地完成,那么 Completion 也會Completion 如果第一個完成的塊以異常完成,則Completion將以相同的異常完成。 隨意調整您的特定需求。 :)

這是一個專門使用 TPL 數據流庫本身工具的解決方案。 您可以創建一個TransformBlock來“處理”您要觀察的ActionBlock 處理一個塊意味着簡單地等待它的完成。 因此TransformBlock接受不完整的塊,並輸出與已完成的塊相同的塊。 TransformBlock必須配置為無限並行和容量,並禁用排序,以便同時觀察所有塊,並立即返回每個完成的塊。

var allBlocks = new TransformBlock<ActionBlock<IMsg>, ActionBlock<IMsg>>(async block =>
{
    try { await block.Completion; }
    catch { }
    return block;
}, new ExecutionDataflowBlockOptions()
{
    MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
    EnsureOrdered = false
});

然后在LinkerThingBob.LinkTo方法中,將創建的ActionBlock發送到TransformBlock

var actionBlock = new ActionBlock<IMsg>(_ => this.Tx(messages, _));
allBlocks.Post(actionBlock);

現在您需要一個目標來接收第一個故障塊。 WriteOnceBlock非常適合這一角色,因為它確保最多接收一個故障塊。

var firstFaulted = new WriteOnceBlock<ActionBlock<IMsg>>(x => x);

allBlocks.LinkTo(firstFaulted, block => block.Completion.IsFaulted);

最后,您可以在任何地方await WriteOnceBlock的完成。 它將在收到錯誤塊后立即完成,或者如果它從未收到錯誤塊,則可能永遠不會完成。

await firstFaulted.Completion;

等待之后,如果需要,您還可以獲得故障塊。

ActionBlock<IMsg> faultedBlock = firstFaulted.Receive();

WriteOnceBlock在轉發消息時的行為方式很特別。 與大多數其他塊不同,您可以多次調用它的Receive方法,並且您總是會得到它包含的相同的單個項目(它不會在第一個Receive之后從其緩沖區中刪除)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM