我的应用程序使用正则表达式并行搜索许多文件,await Task.WhenAll(filePaths.Select(FindThings));
代替FindThings
,它花费大部分时间执行正则表达式搜索,因为这些文件的大小可能有数百MB。
static async Task FindThings(string path) {
string fileContent = null;
try
{
using (var reader = File.OpenText(path))
fileContent = await reader.ReadToEndAsync();
}
catch (Exception e)
{
WriteLine(lineIndex, "{0}: Error {1}", filename, e);
return;
}
var exitMatches = _exitExp.Matches(fileContent);
foreach (Match exit in exitMatches)
{
if (_taskDelay > 0)
await Task.Delay(_taskDelay);
// [...]
- 是否有正则表达式的异步版本或任何方法可以使其与任务正确配合?
为什么这很重要
我收到的很多回复表明我没有澄清为什么这很重要。以这个示例程序(使用 Nitro.Async 库)为例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nito.AsyncEx;
namespace Scrap
{
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync(args));
}
static async void MainAsync(string[] args)
{
var tasks = new List<Task>();
var asyncStart = DateTime.Now;
tasks.Add(Task.WhenAll(Enumerable.Range(0, 10).Select(i =>
ShowIndexAsync(i, asyncStart))));
var start = DateTime.Now;
tasks.Add(Task.WhenAll(Enumerable.Range(0, 10).Select(i =>
ShowIndex(i, start))));
await Task.WhenAll(tasks);
Console.ReadLine();
}
static async Task ShowIndexAsync(int index, DateTime start)
{
Console.WriteLine("ShowIndexAsync: {0} ({1})",
index, DateTime.Now - start);
await Task.Delay(index * 100);
Console.WriteLine("!ShowIndexAsync: {0} ({1})",
index, DateTime.Now - start);
}
static Task ShowIndex(int index, DateTime start)
{
return Task.Factory.StartNew(() => {
Console.WriteLine("ShowIndex: {0} ({1})",
index, DateTime.Now - start);
Task.Delay(index * 100).Wait();
Console.WriteLine("!ShowIndex: {0} ({1})",
index, DateTime.Now - start);
});
}
}
}
因此,这会调用 ShowIndexAsync 10 次,然后调用 ShowIndex 10 次并等待它们完成。
ShowIndexAsync 是“与核心异步”,而 ShowIndex 不是,但它们都对任务进行操作。这里的阻塞操作是 Task.Delay,区别在于一个等待该任务,而另一个 .Wait() 则在任务内部进行。
您希望第一个队列(ShowIndexAsync)首先完成,但您是错误的。
ShowIndexAsync: 0 (00:00:00.0060000)
!ShowIndexAsync: 0 (00:00:00.0070000)
ShowIndexAsync: 1 (00:00:00.0080000)
ShowIndexAsync: 2 (00:00:00.0110000)
ShowIndexAsync: 3 (00:00:00.0110000)
ShowIndexAsync: 4 (00:00:00.0120000)
ShowIndexAsync: 5 (00:00:00.0130000)
ShowIndexAsync: 6 (00:00:00.0130000)
ShowIndexAsync: 7 (00:00:00.0140000)
ShowIndexAsync: 8 (00:00:00.0150000)
ShowIndexAsync: 9 (00:00:00.0150000)
ShowIndex: 0 (00:00:00.0020000)
!ShowIndex: 0 (00:00:00.0020000)
ShowIndex: 1 (00:00:00.0030000)
!ShowIndex: 1 (00:00:00.1100000)
ShowIndex: 2 (00:00:00.1100000)
!ShowIndex: 2 (00:00:00.3200000)
ShowIndex: 3 (00:00:00.3200000)
!ShowIndex: 3 (00:00:00.6220000)
ShowIndex: 4 (00:00:00.6220000)
!ShowIndex: 4 (00:00:01.0280000)
ShowIndex: 5 (00:00:01.0280000)
!ShowIndex: 5 (00:00:01.5420000)
ShowIndex: 6 (00:00:01.5420000)
!ShowIndex: 6 (00:00:02.1500000)
ShowIndex: 7 (00:00:02.1510000)
!ShowIndex: 7 (00:00:02.8650000)
ShowIndex: 8 (00:00:02.8650000)
!ShowIndex: 8 (00:00:03.6660000)
ShowIndex: 9 (00:00:03.6660000)
!ShowIndex: 9 (00:00:04.5780000)
!ShowIndexAsync: 1 (00:00:04.5950000)
!ShowIndexAsync: 2 (00:00:04.5960000)
!ShowIndexAsync: 3 (00:00:04.5970000)
!ShowIndexAsync: 4 (00:00:04.5970000)
!ShowIndexAsync: 5 (00:00:04.5980000)
!ShowIndexAsync: 6 (00:00:04.5990000)
!ShowIndexAsync: 7 (00:00:04.5990000)
!ShowIndexAsync: 8 (00:00:04.6000000)
!ShowIndexAsync: 9 (00:00:04.6010000)
为什么会发生这种事?
任务调度程序只会使用这么多的实际线程。 “await”编译为协作多任务状态机。如果您有未等待的阻塞操作,在此示例中Task.Delay(...).Wait()
,但在我的问题中,正则表达式匹配,它不会合作并让任务调度程序正确管理任务。
如果我们将示例程序更改为:
static async void MainAsync(string[] args)
{
var asyncStart = DateTime.Now;
await Task.WhenAll(Enumerable.Range(0, 10).Select(i =>
ShowIndexAsync(i, asyncStart)));
var start = DateTime.Now;
await Task.WhenAll(Enumerable.Range(0, 10).Select(i =>
ShowIndex(i, start)));
Console.ReadLine();
}
然后我们的输出变为:
ShowIndexAsync: 0 (00:00:00.0050000)
!ShowIndexAsync: 0 (00:00:00.0050000)
ShowIndexAsync: 1 (00:00:00.0060000)
ShowIndexAsync: 2 (00:00:00.0080000)
ShowIndexAsync: 3 (00:00:00.0090000)
ShowIndexAsync: 4 (00:00:00.0090000)
ShowIndexAsync: 5 (00:00:00.0100000)
ShowIndexAsync: 6 (00:00:00.0110000)
ShowIndexAsync: 7 (00:00:00.0110000)
ShowIndexAsync: 8 (00:00:00.0120000)
ShowIndexAsync: 9 (00:00:00.0120000)
!ShowIndexAsync: 1 (00:00:00.1150000)
!ShowIndexAsync: 2 (00:00:00.2180000)
!ShowIndexAsync: 3 (00:00:00.3160000)
!ShowIndexAsync: 4 (00:00:00.4140000)
!ShowIndexAsync: 5 (00:00:00.5190000)
!ShowIndexAsync: 6 (00:00:00.6130000)
!ShowIndexAsync: 7 (00:00:00.7190000)
!ShowIndexAsync: 8 (00:00:00.8170000)
!ShowIndexAsync: 9 (00:00:00.9170000)
ShowIndex: 0 (00:00:00.0030000)
!ShowIndex: 0 (00:00:00.0040000)
ShowIndex: 3 (00:00:00.0060000)
ShowIndex: 4 (00:00:00.0090000)
ShowIndex: 2 (00:00:00.0100000)
ShowIndex: 1 (00:00:00.0100000)
ShowIndex: 5 (00:00:00.0130000)
ShowIndex: 6 (00:00:00.0130000)
ShowIndex: 7 (00:00:00.0150000)
ShowIndex: 8 (00:00:00.0180000)
!ShowIndex: 7 (00:00:00.7660000)
!ShowIndex: 6 (00:00:00.7660000)
ShowIndex: 9 (00:00:00.7660000)
!ShowIndex: 2 (00:00:00.7660000)
!ShowIndex: 5 (00:00:00.7660000)
!ShowIndex: 4 (00:00:00.7660000)
!ShowIndex: 3 (00:00:00.7660000)
!ShowIndex: 1 (00:00:00.7660000)
!ShowIndex: 8 (00:00:00.8210000)
!ShowIndex: 9 (00:00:01.6700000)
请注意,异步调用如何具有良好的均匀结束时间分布,但非异步代码却没有。任务调度程序被阻塞,因为它不会创建额外的实际线程,因为它期待合作。
我不希望它花费更少的CPU时间或类似的东西,但我的目标是使FindThings
协作庄园中的多任务,即使其“与核心异步”。