我刚刚开始在c#中处理异步编程,我开始阅读有关异步方法和等待的内容。
在下面的代码块中,WPF应用程序从用户获取输入,将其保存到Bin目录中的文件,然后将其读回文本框。我不得不使用async
方法进行读写,但我还需要在await
和WriteText
方法中的方法中实现ReadText
。
您能否简要介绍一下如何在此代码中实现async和await的使用?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnWriteFile_Click(object sender, RoutedEventArgs e)
{
await WriteFile();
}
private async void btnReadFile_Click(object sender, RoutedEventArgs e)
{
await ReadFile();
}
public async Task WriteFile()
{
string filePath = @"SampleFile.txt";
string text = txtContents.Text;
Task task1 = new Task( () => WriteTextAsync(filePath, text));
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
//sourceStream.BeginWrite(encodedText, 0, encodedText.Length);
await ?? sourceStream.BeginWrite(encodedText, 0, encodedText.Length, null, null);
};
}
public async Task ReadFile()
{
string filePath = @"SampleFile.txt";
if (File.Exists(filePath) == false)
{
MessageBox.Show(filePath + " not found", "File Error", MessageBoxButton.OK);
}
else
{
try
{
string text = await ReadText(filePath);
txtContents.Text = text;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
private async Task<string> ReadText(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = sourceStream.Read(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
}
}
答案 0 :(得分:4)
让我们一次拿一个:
public async Task WriteFile()
{
string filePath = @"SampleFile.txt";
string text = txtContents.Text;
Task task1 = new Task( () => WriteTextAsync(filePath, text));
}
这里有什么任务1?你需要实际运行它并等待它:
public async Task WriteFile()
{
string filePath = @"SampleFile.txt";
string text = txtContents.Text;
Task task1 = new Task( () => await WriteTextAsync(filePath, text));
await task1;
}
但是等等!我们正在创建Task
,创建Task
,然后等待Task
。为什么不首先返回Task
?
public Task WriteFile()
{
string filePath = @"SampleFile.txt";
string text = txtContents.Text;
return WriteTextAsync(filePath, text);
}
请注意,async
使我们更容易创建在Task
中执行某些操作的方法,但如果您已经拥有Task
,则会浪费时间。
另外,作为惯例,您应该将所有异步方法命名为Async
。这在这里更是如此,因为您与签名中的其他WriteTextAsync
不同:
public Task WriteTextAsync()
{
return WriteTextAsync(@"SampleFile.txt", txtContents.Text);
}
实际上,如果您使用非异步void WriteText(string filePath, string text)
从非异步void WriteText()
调用它,则与此无异。这里什么都没有。
现在,更多参与WriteTextAsync
:
因为我们现在已经完成了任务,所以我们根本不需要使用旧的BeginWrite
(但请参见下文),我们只是使用WriteAsync
类似于我们如何使用Write
:
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
ReadFile()
没问题。让我们来看看它的内容:
private async Task<string> ReadText(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = sourceStream.Read(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
}
这会奏效,但不会有任何收获。不过,我们可以将Read
替换为await
ReadAsync
:
private async Task<string> ReadText(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
}
对于非同步版本的更好的整体解决方案,面对可能由Read
分割的字符的编码,更简单且更具弹性,可能是使用ReadToEnd()
。同样,这里更好的版本是使用ReadToEndAsync()
:
private async Task<string> ReadText(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096))
{
using(var rdr = new StreamReader(sourceStream, Encoding.Unicode))
{
return await rdr.ReadToEndAsync();
}
}
}
请注意,虽然我们正在返回await
任务的结果,但在这种情况下我们不能将此替换为return rdr.ReadToEndAsync()
,因为我们会在using
之前离开ReadToEndAsync()
await
实际上已经完成了。我们需要IDisposable.Dispose()
来确保我们获得了实际结果,然后进行using
调用,留下BeginXxx
调用。
EndXxx
... stream.WriteAsync()
)中使用TPL(异步):我们假设我们没有stream.BeginWrite()
并且必须使用stream.EndWrite()
和TaskFactory.FromAsync
。如果您使用较旧的库,可能会发生类似这样的事情。
我们可以使用Task
创建一个private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await Task.Factory.FromAsync(sourceStream.BeginWrite, sourceStream.EndWrite, encodedText, 0, encodedText.Length, null);
}
}
来包装旧方法。因此:
private async Task<string> ReadText(string filePath)
{
using(FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize:4096))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while((numRead = await Task<int>.Factory.FromAsync(sourceStream.BeginRead, sourceStream.EndRead, buffer, 0, buffer.Length, null)) != 0)
{
sb.Append(Encoding.Unicode.GetString(buffer, 0, numRead);
}
return sb.ToString();
}
}
和
XxxAsync
这显然比使用await
方法更加复杂,我们可以BeginXxx
,但它仍然比调用EndXxx
然后处理ReadText
更简单。回调,尤其是在上面BeginXxx
的情况下,这会导致另一个循环进入select avg (price) from products where price > 30
。
答案 1 :(得分:0)
FileStream类具有用于读取和写入流的异步方法:ReadAsync
和WriteAsync
,因此您需要做的就是在代码中替换这些方法,并在其前面添加await:
private async Task<string> ReadText(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
}
和
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
};
}
我确信这两种方法可以进一步简化,但这应该让你开始使用异步方法。
所以你知道,如果你试图使用一个没有异步方法的类,并且你想在一个单独的线程上执行该任务,你仍然可以在这个中使用async / await时尚:
private async Task<string> ReadText(string filePath)
{
return await Task.Run(() =>
{
return File.ReadAllText("textfilepath.txt");
});
}