[英]Create list of ActionBlock<T> that will complete when any fail
在可以對“空”任務列表調用 await 的情況下。
我如何等待Task<T>
列表,然后將新任務添加到等待列表,直到任務失敗或完成。
我確信這個問題必須有Awaiter
或CancellationTokenSource
解決方案。
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.