在一次设置最大运行任务时等待多个异步任务
所以我刚开始尝试理解异步,任务,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
介绍 ,有更多信息。
您的代码有几个问题:
-
在Task上使用Wait()就像是同步运行一样,因此你只会注意到在完成所有操作并重新启用按钮时UI会做出反应。 您需要等待异步方法才能真正运行异步。 更重要的是,如果一个方法像Web请求一样进行IO绑定工作,那么启动一个新的线程池线程(使用Task.Factory.StartNew)是多余的,并且是浪费资源。
-
您的按钮单击事件处理程序需要标记为异步,以便您可以在方法内等待。
-
为了清楚起见,我已经清理了一些代码,使用新的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