我有3个文件,每个文件100万行,正在逐行读取它们。没有处理,只是在阅读,因为我只是在试用。
如果我同步执行此操作,则需要1秒钟。如果我改用Threads(每个文件一个),则速度会更快(代码不在下面,但我只是创建了一个新的Thread并为每个文件启动了它)。
当我更改为异步时,它的耗时是40秒的40倍。如果我添加任何工作来进行实际处理,则无法看到如何在同步上使用异步,或者我是否想要使用线程的响应式应用程序。
还是我在这段代码中做了根本上错误的事情,而不是异步的意图?
谢谢。
class AsyncTestIOBound
{
Stopwatch sw = new Stopwatch();
internal void Tests()
{
DoSynchronous();
DoASynchronous();
}
#region sync
private void DoSynchronous()
{
sw.Restart();
var start = sw.ElapsedMilliseconds;
Console.WriteLine($"Starting Sync Test");
DoSync("Addresses", "SampleLargeFile1.txt");
DoSync("routes ", "SampleLargeFile2.txt");
DoSync("Equipment", "SampleLargeFile3.txt");
sw.Stop();
Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
Console.ReadKey();
}
private long DoSync(string v, string filename)
{
string line;
long counter = 0;
using (StreamReader file = new StreamReader(filename))
{
while ((line = file.ReadLine()) != null)
{
counter++;
}
}
Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {counter}");
return counter;
}
#endregion
#region async
private void DoASynchronous()
{
sw.Restart();
var start = sw.ElapsedMilliseconds;
Console.WriteLine($"Starting Sync Test");
Task a=DoASync("Addresses", "SampleLargeFile1.txt");
Task b=DoASync("routes ", "SampleLargeFile2.txt");
Task c=DoASync("Equipment", "SampleLargeFile3.txt");
Task.WaitAll(a, b, c);
sw.Stop();
Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
Console.ReadKey();
}
private async Task<long> DoASync(string v, string filename)
{
string line;
long counter = 0;
using (StreamReader file = new StreamReader(filename))
{
while ((line = await file.ReadLineAsync()) != null)
{
counter++;
}
}
Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {counter}");
return counter;
}
#endregion
}
答案 0 :(得分:2)
几件事。首先,我将使用async方法一次读取所有行,以便您仅等待一次(而不是每行)。
private async Task<long> DoASync(string v, string filename)
{
string lines;
long counter = 0;
using (StreamReader file = new StreamReader(filename))
{
lines = await reader.ReadToEndAsync();
}
Console.WriteLine($"{v}: T{Thread.CurrentThread.ManagedThreadId}: Lines: {lines.Split('\n').Length}");
return counter;
}
接下来,您还可以分别等待每个任务。这将导致您的CPU一次只专注于一个,而不可能在三个之间切换,这将导致更多的开销。
private async void DoASynchronous()
{
sw.Restart();
var start = sw.ElapsedMilliseconds;
Console.WriteLine($"Starting Sync Test");
await DoASync("Addresses", "SampleLargeFile1.txt");
await DoASync("routes ", "SampleLargeFile2.txt");
await DoASync("Equipment", "SampleLargeFile3.txt");
sw.Stop();
Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
Console.ReadKey();
}
之所以看到较低的性能,是因为等待如何与CPU负载一起工作。对于每个新行,这将导致CPU使用率增加。异步机制增加了处理,分配和同步。另外,我们需要两次而不是一次地转换到内核模式(首先启动IO,然后出队IO完成通知)。
答案 1 :(得分:2)
由于您在一个巨大的循环中多次使用await
(在您的情况下,循环通过“ SampleLargeFile”的每一行),因此您要进行大量的上下文切换,因此开销可能真的很糟糕
对于每一行,您的代码可能正在每个文件之间切换。如果您的计算机使用硬盘驱动器,则情况可能更糟。想象一下HD的头部变得疯狂。
使用普通线程时,您不会切换每行的上下文。
要解决此问题,只需一次运行即可读取文件。您仍然可以使用async/await
(ReadToEndAsync()
)并获得良好的性能。
编辑
因此,您正在尝试使用异步计数文本文件上的行,对吧?
尝试此操作(无需将整个文件加载到内存中):
private async Task<int> CountLines(string path)
{
int count = 0;
await Task.Run(() =>
{
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
while (sr.ReadLine() != null)
{
count++;
}
}
});
return count;
}