Csharp/C#教程:在一次设置最大运行任务时等待多个异步任务分享


在一次设置最大运行任务时等待多个异步任务

所以我刚开始尝试理解异步,任务,lambda等等,我无法让它像我想的那样工作。 使用下面的代码我希望它能锁定btnDoWebRequest,将一个未知数量的WebRequests作为任务执行,并且一旦所有任务完成,就解锁btnDoWebRequest。 但是我只希望最多3个或者我一次运行的任务数量,这部分来自于我有一组任务,一次只运行X.

但是在以多种方式尝试和修改我的代码之后,它将始终立即跳回并重新启用btnDoWebRequest。 当然VS警告我需要等待,目前在“.ContinueWith((任务)”和“await Task.WhenAll(requestInfoList .Select(async i =>”)中的异步,但似乎无法工作在哪里或如何投入所需的等待。当然,因为我仍然在学习,所以我很有可能在这一切都错了,整个事情需要重新修改。所以任何帮助都将非常感激。

谢谢

private SemaphoreSlim maxThread = new SemaphoreSlim(3); private void btnDoWebRequest_Click(object sender, EventArgs e) { btnDoWebRequest.Enabled = false; Task.Factory.StartNew(async () => await DoWebRequest()).Wait(); btnDoWebRequest.Enabled = true; } private async Task DoWebRequest() { List requestInfoList = new List(); for (int i = 0; dataRequestInfo.RowCount - 1 > i; i++) { requestInfoList.Add((requestInfo)dataRequestInfo.Rows[i].Tag); } await Task.WhenAll(requestInfoList .Select(async i => { maxThread.Wait(); Task.Factory.StartNew(() => { var task = Global.webRequestWork(i); }, TaskCreationOptions.LongRunning).ContinueWith((task) => maxThread.Release()); })); } 

首先,默认情况下不要使用Task.Factory.StartNew 。 实际上,应该在async代码中避免这种情况。 如果需要在后台线程上执行代码,请使用Task.Run

在您的情况下,不需要使用Task.Run (或Task.Factory.StartNew )。

从最低级别开始,逐步提升。 您已经有一个异步Web请求方法,我将重命名为WebRequestAsync以遵循基于任务的异步编程命名准则。

接下来,使用SemaphoreSlim上的异步 API来限制它:

 await maxThread.WaitAsync(); try { await Global.WebRequestWorkAsync(i); } finally { maxThread.Release(); } 

为每个请求信息执行此操作(请注意,不需要后台线程):

 private async Task DoWebRequestsAsync() { List requestInfoList = new List(); for (int i = 0; dataRequestInfo.RowCount - 1 > i; i++) { requestInfoList.Add((requestInfo)dataRequestInfo.Rows[i].Tag); } await Task.WhenAll(requestInfoList.Select(async i => { await maxThread.WaitAsync(); try { await Global.WebRequestWorkAsync(i); } finally { maxThread.Release(); } })); } 

最后,从您的UI调用此方法(同样,不需要后台线程):

 private async void btnDoWebRequest_Click(object sender, EventArgs e) { btnDoWebRequest.Enabled = false; await DoWebRequestsAsync(); btnDoWebRequest.Enabled = true; } 

总之,只在需要时才使用Task.Run ; 不要使用Task.Factory.StartNew ,也不要使用Wait (使用await代替)。 我在博客上有一个async介绍 ,有更多信息。

您的代码有几个问题:

  1. 在Task上使用Wait()就像是同步运行一样,因此你只会注意到在完成所有操作并重新启用按钮时UI会做出反应。 您需要等待异步方法才能真正运行异步。 更重要的是,如果一个方法像Web请求一样进行IO绑定工作,那么启动一个新的线程池线程(使用Task.Factory.StartNew)是多余的,并且是浪费资源。

  2. 您的按钮单击事件处理程序需要标记为异步,以便您可以在方法内等待。

  3. 为了清楚起见,我已经清理了一些代码,使用新的SemaphoreSlim WaitAsync并用LINQ查询替换了你的for。 您只能获取前两个点并将其应用于您的代码。

     private SemaphoreSlim maxThread = new SemaphoreSlim(3); private async void btnDoWebRequest_Click(object sender, EventArgs e) { btnDoWebRequest.Enabled = false; await DoWebRequest(); btnDoWebRequest.Enabled = true; } private async Task DoWebRequest() { List requestInfoList = new List(); var requestInfoList = dataRequestInfo.Rows.Select(x => x.Tag).Cast(); var tasks = requestInfoList.Select(async I => { await maxThread.WaitAsync(); try { await Global.webRequestWork(i); } finally { maxThread.Release(); } }); await Task.WhenAll(tasks); 

我为此创建了一个扩展方法。

它可以像这样使用:

 var tt = new List>() { () => Thread.Sleep(300), //Thread.Sleep can be replaced by your own functionality, like calling the website () => Thread.Sleep(800), () => Thread.Sleep(250), () => Thread.Sleep(1000), () => Thread.Sleep(100), () => Thread.Sleep(200), }; await tt.WhenAll(3); //this will let 3 threads run, if one ends, the next will start, untill all are finished. 

扩展方法:

 public static class TaskExtension { public static async Task WhenAll(this List> actions, int threadCount) { var _countdownEvent = new CountdownEvent(actions.Count); var _throttler = new SemaphoreSlim(threadCount); foreach (Func action in actions) { await _throttler.WaitAsync(); Task.Run(async () => { try { await action(); } finally { _throttler.Release(); _countdownEvent.Signal(); } }); } _countdownEvent.Wait(); } } 

我们可以使用SemaphoreSlim轻松实现这一目标。 我创建的扩展方法:

  ///  /// Concurrently Executes async actions for each item of  ///  /// Type of IEnumerable /// instance of "/> /// an async  to execute /// Optional, max numbers of the actions to run in parallel, /// Must be grater than 0 /// A Task representing an async operation /// If the maxActionsToRunInParallel is less than 1 public static async Task ForEachAsyncConcurrent( this IEnumerable enumerable, Func action, int? maxActionsToRunInParallel = null) { if (maxActionsToRunInParallel.HasValue) { using (var semaphoreSlim = new SemaphoreSlim( maxActionsToRunInParallel.Value, maxActionsToRunInParallel.Value)) { var tasksWithThrottler = new List(); foreach (var item in enumerable) { // Increment the number of currently running tasks and wait if they are more than limit. await semaphoreSlim.WaitAsync(); tasksWithThrottler.Add(Task.Run(async () => { await action(item); // action is completed, so decrement the number of currently running tasks semaphoreSlim.Release(); })); } // Wait for all tasks to complete. await Task.WhenAll(tasksWithThrottler.ToArray()); } } else { await Task.WhenAll(enumerable.Select(item => action(item))); } } 

样品用法:

上述就是C#学习教程:在一次设置最大运行任务时等待多个异步任务分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

 await enumerable.ForEachAsyncConcurrent( async item => { await SomeAsyncMethod(item); }, 5); 

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/1018529.html

(0)
上一篇 2022年1月4日
下一篇 2022年1月4日

精彩推荐