C# 在这个异步示例中,我是否做错了什么?
我是异步编程新手,我一直在编写一个小示例来演示如何使用任务编程。我想知道你对样品的看法 我在什么地方堵车吗?我做错什么了吗? 能不能让它变得更好 以下是示例的代码:C# 在这个异步示例中,我是否做错了什么?,c#,asynchronous,task-parallel-library,async-await,C#,Asynchronous,Task Parallel Library,Async Await,我是异步编程新手,我一直在编写一个小示例来演示如何使用任务编程。我想知道你对样品的看法 我在什么地方堵车吗?我做错什么了吗? 能不能让它变得更好 以下是示例的代码: static void Main(string[] args) { const string directory= "files"; IEnumerable<string> files = FindFiles(directory).ToList(); DateTime startAsink =
static void Main(string[] args)
{
const string directory= "files";
IEnumerable<string> files = FindFiles(directory).ToList();
DateTime startAsink = DateTime.Now;
ProcessFilesAsync(files).ContinueWith(r =>
{
r.Result.ToList().ForEach(Console.WriteLine);
Console.WriteLine(DateTime.Now.Ticks - startAsink.Ticks);
});
Console.ReadKey();
}
private static IEnumerable<string> FindFiles(string directoy)
{
return Directory.GetFiles(directory).ToList();
}
private static Task<Tuple<string, int>> ProcessOneFile(string name)
{
return Task.Run(() =>
{
IEnumerable<string> lines = File.ReadLines(name);
int sum = 0;
foreach (var line in lines )
{
sum += line.Split(' ').Length;
}
return new Tuple<string, int>(name, sum);
});
}
private static async Task<IEnumerable<Tuple<string, int>>> ProcessFilesAsync(IEnumerable<string> files)
{
var listOfResults = files.Select(ProcessOneFile);
var task = (await Task.WhenAll(listOfResults)).ToList();
return task;
}
}
static void Main(字符串[]args)
{
const string directory=“files”;
IEnumerable files=FindFiles(directory.ToList();
DateTime startAsink=DateTime.Now;
ProcessFileAsync(文件).ContinueWith(r=>
{
r、 Result.ToList().ForEach(Console.WriteLine);
Console.WriteLine(DateTime.Now.Ticks-startAsink.Ticks);
});
Console.ReadKey();
}
私有静态IEnumerable FindFile(字符串目录)
{
返回Directory.GetFiles(Directory.ToList();
}
私有静态任务ProcessOneFile(字符串名称)
{
返回任务。运行(()=>
{
IEnumerable lines=File.ReadLines(名称);
整数和=0;
foreach(行中的var行)
{
总和+=行分割('')。长度;
}
返回新元组(名称、总和);
});
}
专用静态异步任务ProcessFileAsync(IEnumerable文件)
{
var listOfResults=files.Select(ProcessOneFile);
var task=(wait task.WhenAll(listOfResults)).ToList();
返回任务;
}
}
以下是一种可能的代码异步重构:
static void Main(string[] args)
{
const string directory = "files";
IEnumerable<string> files = FindFiles(directory).ToList();
Stopwatch chrono = new Stopwatch();
chrono.Start();
var tasks = files.Select(f => ProcessOneFileAsync(f)).ToArray();
Task.WaitAll(tasks);
chrono.Stop();
foreach (var t in tasks)
{
Console.WriteLine(t.Result);
}
Console.WriteLine(chrono.ElapsedMilliseconds);
Console.ReadKey();
}
private static IEnumerable<string> FindFiles(string directoy)
{
return Directory.GetFiles(directoy).ToList();
}
private static async Task<Tuple<string, int>> ProcessOneFileAsync(string name)
{
int sum = 0;
using (TextReader file = File.OpenText(name))
{
String line = null;
while ((line = await file.ReadLineAsync()) != null)
{
sum += line.Split(' ').Length;
}
}
return new Tuple<string, int>(name, sum);
}
static void Main(字符串[]args)
{
const string directory=“files”;
IEnumerable files=FindFiles(directory.ToList();
秒表计时=新秒表();
chrono.Start();
var tasks=files.Select(f=>ProcessOneFileAsync(f)).ToArray();
Task.WaitAll(任务);
计时停止();
foreach(任务中的var t)
{
控制台写入线(t.Result);
}
控制台写入线(时间延迟毫秒);
Console.ReadKey();
}
私有静态IEnumerable FindFile(字符串目录)
{
返回Directory.GetFiles(directoy.ToList();
}
专用静态异步任务ProcessOneFileAsync(字符串名称)
{
整数和=0;
使用(TextReader文件=file.OpenText(名称))
{
字符串行=null;
而((line=wait file.readlinesync())!=null)
{
总和+=行分割('')。长度;
}
}
返回新元组(名称、总和);
}
PS:为了获得一致的计时结果,我用秒表代替了日期时间
我在什么地方堵车吗
不,您使用的“一路异步”流似乎是正确的。但是,当您可以通过TextReader.readlinesync
使用其async
兄弟时,您正在使用阻塞API(例如File.ReadLines
)
我做错什么了吗
我能马上发现的是:
Task.Run
的调用,而应该公开一个同步方法,让调用方显式调用Task.Run
。这样,你就不会让他假设这个方法是自然异步的,而实际上它不是async
postfix结尾您可以使用公开自然异步文件读取的API,例如
FileStream
和TextReader
类,而不是使用Task.Run
执行IO绑定操作。这样,您实际上公开了一个async
方法,而不是一个“async-over-sync”方法。我发现它有几个问题。通常,当一个人使用async
时,这是因为他们希望在结果可用时处理这些结果(想想:yield
),而不是等待整个集合完全形成
每次使用ToList
都是对async
和yield
的一记耳光。实际上,您是在声明希望代码在继续之前等待
考虑在FindFiles
方法中使用GetFiles
。如果省略ToList
,GetFiles
将返回一个字符串[]
,该字符串无论如何都实现了IEnumerable
。但更重要的是,您应该将GetFiles
完全替换为EnumerateFiles
。我欢迎您在闲暇时阅读MSDN,但我确实引用了以下内容:
EnumerateFiles和GetFiles方法的区别如下:
使用EnumerateFiles,可以开始枚举名称集合
在归还全部藏品之前;当您使用GetFiles时
必须等待返回整个名称数组,然后才能
访问阵列。因此,当您处理许多文件和
目录、枚举文件可以更高效
因此FindFiles
变成:
private static IEnumerable<string> FindFiles(string directory)
{
return Directory.EnumerateFiles(directory);
}
私有静态IEnumerable FindFile(字符串目录)
{
返回目录。枚举文件(目录);
}
出于同样的原因,您还应该删除对ToList
的所有引用。这是直接处理async
的两个最大问题。这样,您的代码确实可以执行async
,并在项目可用时进行处理
另一件事是,
DateTime.UtcNow
应该用于任何内部计时。不仅是UtcNow
比本地化的DateTime快得多。现在
,它也不受时区转换的任何限制。processfileasync
是异步的。你不用等待。resharper/VS将警告您这一点。你刚才所做的就是开火然后忘记。我不知道你需要什么,但我只是注意到了你。也。您可以使用ContinueWith
,而您应该使用'await(如果您确实使用异步await方法)来告诉我们