For循环中并发的思路(async/await、Task)

问题

最开始的问题,是我需要对一组的网页链接进行下载,一开始单线程的时候,就是一个网页下载,保存,结束,然后下载第二个网页。
结果当然是没有问题的,可是这效率也太低了啊,那就想了几个问题:

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); });
            }
            Task.WaitAll(tasks);
        }

WaitAll会等待所有异步结束,这样,就可以等待你for循环中,所有的异步都结束之后,才会进行下一步操作。

3、控制Task的并发数量

2的内容中,Task是没有数量控制的,如果For循环中有1000个下载链接,然后瞬间Task执行1000个,然后等待。。。想想那个后果。
嗯,事实上,我这边是已经遇到了这个情况,最多的时候500+,程序会直接卡滞数秒。
而效率方面,单线程下载一秒钟2-3个网页,并发之后是5-6个网页,我不知道为什么效率就提高了这么点,而程序卡滞的却很严重。
想想解决的办法?可以限制并发的数量么?很显然,我找到了办法:
https://blog.csdn.net/starfd/article/details/79711915
控制并发执行的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, scheduler);
            }
            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中的并发数,这个回头试试。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
引用提到了使用async/await必须在async函数的作用域下使用,并且在使用await时要注意语法错误。而引用介绍了async/await是ES7引入的一种异步编程方式,它提供了使用同步样式的代码异步访问资源的方式,并且不会阻塞线程。使用async/await可以简化异步编程的代码,使其更易读。同时,async/await是基于Promise的,可以通过try-catch来捕获异常。引用还提到了多个await命令可以统一放在try...catch结构。根据这些信息,可以得出jquery也可以使用async/await来实现异步操作。 使用jquery实现async/await的方式与使用原生JavaScript的方式基本相同。首先,确保在引入jquery之后在async函数的作用域下进行操作。然后,可以使用$.ajax来发送异步请求,并在需要等待其返回结果时使用await关键字。例如,可以将多个并发请求统一放在try...catch结构,如下所示: ```javascript async function main() { try { const result1 = await $.ajax({ url: 'url1', method: 'GET' }); const result2 = await $.ajax({ url: 'url2', method: 'GET' }); const result3 = await $.ajax({ url: 'url3', method: 'GET' }); console.log('Final: ', result1, result2, result3); } catch (error) { console.error(error); } } main(); ``` 在上述代码,使用$.ajax发送了三个并发请求,并使用await关键字等待每个请求的结果。最后,将三个请求的结果输出到控制台。如果发生异常,可以通过try-catch捕获并处理。这样,就可以使用jquery的async/await方式来实现异步操作了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [ES7 async/await](https://blog.csdn.net/JunChow520/article/details/103289715)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [12. ES6 async/await 的使用](https://blog.csdn.net/ladymarry/article/details/127929395)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值