For循环中并发的思路(asyncawait、Task)
C# For循环中并发的思路
问题
最开始的问题,是我需要对⼀组的⽹页链接进⾏下载,⼀开始单线程的时候,就是⼀个⽹页下载,保存,结束,然后下载第⼆个⽹页。
结果当然是没有问题的,可是这效率也太低了啊,那就想了⼏个问题:
1、能不能同时下载多个⽹页?
这个是多线程问题,我⼀开始使⽤的是async/await
看代码:
private async void DownloadText(BookModel book)
{
var directories = directory.GetList<Model.DirectoryModel>(d => d.BookId == book.Id && d.State ==true);
foreach(var directory in directories)
{
await Task.Run(()=>{DownloadText(directory, book);});
}
}
下载⽅法是async ⽅法,说明内部有异步的代码。
await 是异步的位置,for中,异步执⾏这⼀个⽅法,循环注册执⾏之后,for循环就结束了。
这⾥就有⼀个问题。。。我for循环是结束了,可是我下载还没有结束啊,都在异步中呢,能不能在for结束的后⾯,让它等待⼀下,等所有代码结束?
async/await我没有到解决的办法,换⼀个写法。
2、等待所有异步⽅法结束
private void DownloadText(BookModel book)
{
var directories = directory.GetList<Model.DirectoryModel>(d => d.BookId == book.Id && d.State == true);
Task<bool>[] tasks = new Task<bool>[directories.Count];
for(int i =0; i < directories.Count; i++)
{
//这⼀步是必须的,不能将directories[i]直接放到Task中做DownloadText的参数,否者i会每次循环后发⽣变化,传到DownloadText⾥⾯就不是你想要传的那个对象了。
var dir = directories[i];
tasks[i]= Task<bool>.Factory.StartNew(()=>{return DownloadText(dir, book);});
//这种写法是不可⾏的,i会被外部变化,directories[i]传⼊的不⼀定是你预期的那个参数
//tasks[i] = Task<bool>.Factory.StartNew(() => { return DownloadText(directories[i], book); });
await和async使用方法}
Task.WaitAll(tasks);
}
WaitAll会等待所有异步结束,这样,就可以等待你for循环中,所有的异步都结束之后,才会进⾏下⼀步操作。
3、控制Task的并发数量
2的内容中,Task是没有数量控制的,如果For循环中有1000个下载链接,然后瞬间Task执⾏1000个,然后等待。。。想想那个后果。嗯,事实上,我这边是已经遇到了这个情况,最多的时候500+,程序会直接卡滞数秒。
⽽效率⽅⾯,单线程下载⼀秒钟2-3个⽹页,并发之后是5-6个⽹页,我不知道为什么效率就提⾼了这么点,⽽程序卡滞的却很严重。
想想解决的办法?可以限制并发的数量么?很显然,我到了办法:
控制并发执⾏的Task数量
需要在NuGet中下载⼀个MSFT.ParallelExtensionsExtras,直接NuGet中搜索就可以了。
然后定义⼀个并发数MaxTask,传到Task.Factory.StartNew()⾥⾯去。
private void DownloadText(BookModel book)
{
DownLoadTextChanging?.Invoke(book, null);
var directories = directory.GetList<Model.DirectoryModel>(d => d.BookId == book.Id && d.State == true && d.Len == null);
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
//记录⼀下,单线程下载,⼀秒钟下载2-3章。
//异步下载⼀秒钟5-6章,⽽且程序很容易卡滞。
Task<bool>[] tasks = new Task<bool>[directories.Count];
for(int i =0; i < directories.Count; i++)
{
//这⼀步是必须的,不能将directories[i]直接放到Task中做DownloadText的参数,否者i会每次循环后发⽣变化,传到DownloadText⾥⾯就不是你想要传的那个对象了。
var dir = directories[i];
tasks[i]= Task<bool>.Factory.StartNew(()=>{return DownloadText(dir, book);}, CancellationToken.None, TaskCreationOptions.None, schedule r);
}
Task.WaitAll(tasks);
DownLoadTextChanged?.Invoke(book, null);
}
都不需要太多,我异步的数量写了2,就同样⼀秒钟可以下载5-6章,和刚才没有限制的并发是⼀样的,或许Task其内部有默认的数量限制?
这个暂且不知,但我限制了数量之后,效率没有降低,程序却已经不会卡了。
4、for循环中执⾏固定数量的并⾏
先说明,这个我还没有测试,我在论坛⾥询问,刚刚看到这个回复,先写上来,我回头试⼀下。
Parallel.ForEach(list, new ParallelOptions(){MaxDegreeOfParallelism=20}, x => Console.WriteLine(x.Name));
new ParallelOptions() {MaxDegreeOfParallelism=20 } 代表线程数量,看字⾯意思,应该是可以指定For中的并发数,这个回头试试。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。