我想請教C#方面的專家開發者。我有三個經常性的任務,我的程式需要做。任務2依賴于任務1,任務3依賴于任務2,但任務1不需要等待其他兩個任務完成就可以再次啟動(程式是連續運行的)。由于每個任務都需要一些時間,我想在一個執行緒或一個C#Task中運行每個任務。一旦任務1完成,任務2開始,任務1再次啟動......等等。
我不確定實作這一目標的最佳方式是什么。我希望有人能在這方面給我指導。
uj5u.com熱心網友回復:
實作這一目標的一種方法是使用名為任務并行庫的東西。它提供了一組類,允許您將您的任務安排為 "塊"。你創建一個方法,按順序執行A、B和C,然后TPL將負責同時運行該方法的多個呼叫。下面是一個小的例子:
async Task Main()
{
var actionBlock = new ActionBlock<int> (DoTasksAsync, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 2 //這是將運行的DoTasksAsync的同時執行次數。
};
await actionBlock.SendAsync(1)。
await actionBlock.SendAsync(2)。
actionBlock.Complete()。
await actionBlock.Completion;
}
async Task DoTasksAsync(int input)>
{
await DoTaskAAsync()。
await DoTaskBAsync()。
await DoTaskCAsync();
}
uj5u.com熱心網友回復:
我可能會使用某種佇列模式。
我不確定任務1是否是執行緒安全的要求,所以我將保持簡單:
正如其中一條評論所提到的,你可能應該能夠在你的代碼中成功使用async/await。特別是在任務 2 和 3 之間。請注意,任務1可以與任務2和3并行運行,因為它不依賴于任何其他任務。
uj5u.com熱心網友回復:
你可以使用下面的ParallelLoop方法。這個方法啟動了一個異步作業流,三個任務是相互平行呼叫的,但對它們自己是順序呼叫的。因此,你不需要在每個任務中添加同步,除非某些任務產生的全域副作用在其他任務中是可見的。
這些任務在ThreadPool上被呼叫,使用Task.Run方法。
// <summary>/span>
//在ThreadPool上重復呼叫三個動作,其中
//動作2取決于動作1,動作3取決于動作2。
//每個動作都是依次對自己進行呼叫。
// </summary>/span>
public static async Task ParallelLoop< TResult1, TResult2> (
Func<TResult1> action1,
Func<TResult1, TResult2> action2,
Action<TResult2> action3,
CancellationToken cancellationToken = default)。
{
//引數驗證省略。
var task1 = Task.FromResult<TResult1>(default)。
var task2 = Task.FromResult<TResult2>(default);
var task3 = Task.CompletedTask。
try
{
int counter = 0;
while (true)
{
counter 。
var result1 = await task1.ConfigureAwait(false) 。
cancellationToken.ThrowIfCancellationRequested()。
task1 = Task.Run(action1); //重新啟動任務1。
if (counter <= 1) continue; //在第一個回圈中 result1是未定義的。
var result2 = await task2.ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested()。
task2 = Task.Run(() => action2(result1)); //重啟任務2。
if (counter <= 2) continue; //在第二個回圈中 result2是未定義的。
await task3.ConfigureAwait(false)。
cancellationToken.ThrowIfCancellationRequested()。
task3 = Task.Run(() => action3(result2)); //restart the task3。
}
}
最終
{
//防止fire-and-forget
任務 allTasks = Task.WhenAll(task1, task2, task3);
try { await allTasks.ConfigureAwait(false); } catch { allTasks.Wait(); }
//在AggregateException中傳播所有錯誤。
}
}
在實作中存在一個明顯的模式,這使得添加具有三個以上動作的多載變得非常簡單。每個添加的動作都需要它自己的通用型別引數(TResult3, TResult4等等)。
使用實體:
var cts = new CancellationTokenSource()。
任務 loopTask = ParallelLoop(() =>
{
//第一個任務
Thread.Sleep(1000); //模擬同步作業。
return "OK"; //傳遞給第二個任務的結果。
}, result =>
{
//第二項任務
Thread.Sleep(1000); //Simulates synchronous work。
return result "!"; //傳遞給第三個任務的結果。
}, result =>
{
//第三項任務。
Thread.Sleep(1000); //模擬同步作業。
}, cts.Token)。
如果任何一個任務失敗,整個回圈將停止(loopTask.Exception 包含錯誤)。由于任務之間相互依賴,從一個失敗的任務中恢復過來是不可能的1。你可以做的是通過Polly Retry策略來執行整個回圈,以確保在失敗的情況下,回圈將被重新投胎。如果你不熟悉Polly庫,你可以使用下面這個簡單而無特色的RetryUntilCanceled方法:
public static async Task RetryUntilCanceled(Func< 任務> 行動。
CancellationToken cancellationToken)。
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested()。
try { await action().ConfigureAwait(false); } }
catch { if (cancellationToken.IsCancellationRequested) throw; }
}
使用方法:
Task loopTask = RetryUntilCanceled(() => ParallelLoop(() =>
{
//...。
}, cts.Token), cts.Token)。
在退出行程之前,建議你Cancel() CancellationTokenSource和Wait()(或await)loopTask,以使回圈優雅地終止。否則,一些任務可能會在作業中被中止。
1 實際上,通過Polly的Retry策略來執行每個單獨的任務是可能的,而且可能更可取。并行回圈將被暫停,直到失敗的任務被重試成功。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/325319.html
標籤:
上一篇:當滿足一個條件時終止執行緒
下一篇:在java中多執行緒的簡單問題
