![](/img/trans.png)
[英]“bounded” BatchBlock => ActionBlock. How to complete the proper way?
[英]How to force an ActionBlock to complete fast
根據文檔:
當數據流塊當前未處理消息並且已保證不再處理任何消息時,數據流塊被視為已完成。
在我的情況下,這種行為並不理想。 我希望能夠隨時取消作業,但是每個單獨操作的處理需要很長時間。 因此,當我取消令牌時,效果不會立即生效。 我必須等待當前處理的項目完成。 我無法直接取消操作,因為我使用的 API 是不可取消的。 我可以做任何事情使該塊忽略當前正在運行的操作並立即完成嗎?
這是一個演示我的問題的示例。 令牌在 500 毫秒后被取消,每個動作的持續時間為 1000 毫秒:
static async Task Main()
{
var cts = new CancellationTokenSource(500);
var block = new ActionBlock<int>(async x =>
{
await Task.Delay(1000);
}, new ExecutionDataflowBlockOptions() { CancellationToken = cts.Token });
block.Post(1); // I must wait for this one to complete
block.Post(2); // This one is ignored
block.Complete();
var stopwatch = Stopwatch.StartNew();
try
{
await block.Completion;
}
catch (OperationCanceledException)
{
Console.WriteLine($"Canceled after {stopwatch.ElapsedMilliseconds} msec");
}
}
Output:
1035 毫秒后取消
所需的 output 將在約 500 毫秒后取消。
根據您的評論摘錄...:
在取消請求的情況下我想要發生的是忽略當前正在運行的工作項。 我已經不在乎了,為什么還要等呢?
...並假設您確實可以讓任務運行,您可以簡單地將您希望調用的作業包裝在另一個Task
中,該任務將不斷輪詢取消或完成,然后取消該Task
。 看一下下面的“概念驗證”代碼,它將“長時間運行”的任務包裝在另一個“任務”任務中,不斷輪詢包裝的任務以完成,並使用 CancellationToken 取消(完全“刺激” the-moment”狀態,你當然會想要重新調整它):
public class LongRunningTaskSource
{
public Task LongRunning(int milliseconds)
{
return Task.Run(() =>
{
Console.WriteLine("Starting long running task");
Thread.Sleep(3000);
Console.WriteLine("Finished long running task");
});
}
public Task LongRunningTaskWrapper(int milliseconds, CancellationToken token)
{
Task task = LongRunning(milliseconds);
Task wrapperTask = Task.Run(() =>
{
while (true)
{
//Check for completion (you could, of course, do different things
//depending on whether it is faulted or completed).
if (!(task.Status == TaskStatus.Running))
break;
//Check for cancellation.
if (token.IsCancellationRequested)
{
Console.WriteLine("Aborting Task.");
token.ThrowIfCancellationRequested();
}
}
}, token);
return wrapperTask;
}
}
使用以下代碼:
static void Main()
{
LongRunningTaskSource longRunning = new LongRunningTaskSource();
CancellationTokenSource cts = new CancellationTokenSource(1500);
Task task = longRunning.LongRunningTaskWrapper(3000, cts.Token);
//Sleep long enough to let things roll on their own.
Thread.Sleep(5000);
Console.WriteLine("Ended Main");
}
...產生以下 output:
Starting long running task
Aborting Task.
Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
Finished long running task
Ended Main
被包裝的任務顯然是在它自己的好時機完成的。 如果您對此沒有任何疑問,但通常情況並非如此,希望這應該符合您的需求。
作為補充示例,運行以下代碼(讓包裝的任務在超時之前完成):
static void Main()
{
LongRunningTaskSource longRunning = new LongRunningTaskSource();
CancellationTokenSource cts = new CancellationTokenSource(3000);
Task task = longRunning.LongRunningTaskWrapper(1500, cts.Token);
//Sleep long enough to let things roll on their own.
Thread.Sleep(5000);
Console.WriteLine("Ended Main");
}
...產生以下 output:
Starting long running task
Finished long running task
Ended Main
因此,任務在超時之前開始並完成,無需取消任何內容。 當然,等待時沒有任何阻塞。 當然,正如您可能已經知道的那樣,如果您知道在長期運行的代碼中幕后使用了什么,那么在必要時最好進行清理。
希望您可以修改此示例以將類似的內容傳遞給您的 ActionBlock。
我不熟悉 TPL Dataflow 庫,所以這當然只是一種解決方法。 此外,如果您擁有的只是一個同步方法調用,而您根本沒有任何影響,那么您顯然需要兩個任務。 一個包裝器任務用於包裝同步調用,另一個用於包裝包裝器任務以包括連續狀態輪詢和取消檢查。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.