C# 正确使用async和Wait

C# 正确使用async和Wait,c#,async-await,C#,Async Await,我刚刚开始讨论c#中的异步编程,我开始阅读异步方法并等待 在下面的这段代码中,WPF应用程序接收用户的输入,将其保存到Bin目录中的文件中,并将其读回文本框。我必须使用async方法进行读写,但我还需要在WriteText和ReadText方法中的方法中实现wait 你能给我一个简短的解释,说明我应该如何在这段代码中实现async和Wait的使用吗 public partial class MainWindow : Window { public MainWindow() {

我刚刚开始讨论c#中的异步编程,我开始阅读异步方法并等待

在下面的这段代码中,WPF应用程序接收用户的输入,将其保存到Bin目录中的文件中,并将其读回文本框。我必须使用
async
方法进行读写,但我还需要在
WriteText
ReadText
方法中的方法中实现
wait

你能给我一个简短的解释,说明我应该如何在这段代码中实现async和Wait的使用吗

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();
        }
    }
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
私有异步无效btnWriteFile\u单击(对象发送方,路由目标)
{
等待WriteFile();
}
私有异步无效btnReadFile_单击(对象发送方,路由目标)
{
等待ReadFile();
}
公共异步任务WriteFile()
{
字符串filePath=@“SampleFile.txt”;
string text=txtContents.text;
Task task1=新任务(()=>WriteTextAsync(文件路径,文本));
}
专用异步任务WriteTextAsync(字符串文件路径,字符串文本)
{
byte[]encodedText=Encoding.Unicode.GetBytes(文本);
使用(FileStream sourceStream=newfilestream(filePath,
FileMode.Create,FileAccess.Write,FileShare.None,
bufferSize:4096,UseAync:true)
{
//sourceStream.BeginWrite(encodedText,0,encodedText.Length);
wait??sourceStream.BeginWrite(encodedText,0,encodedText.Length,null,null);
};
}
公共异步任务ReadFile()
{
字符串filePath=@“SampleFile.txt”;
if(File.Exists(filePath)==false)
{
显示(文件路径+“未找到”,“文件错误”,MessageBox按钮.OK);
}
其他的
{
尝试
{
字符串文本=等待读取文本(文件路径);
txtContents.Text=文本;
}
捕获(例外情况除外)
{
Debug.WriteLine(例如消息);
}
}
}
专用异步任务ReadText(字符串文件路径)
{
使用(FileStream sourceStream=newfilestream(filePath,
FileMode.Open,FileAccess.Read,FileShare.Read,
缓冲区大小:4096)
{
StringBuilder sb=新的StringBuilder();
字节[]缓冲区=新字节[0x1000];
国际货币联盟;
while((numRead=sourceStream.Read(buffer,0,buffer.Length))!=0)
{
string text=Encoding.Unicode.GetString(缓冲区,0,numRead);
附加(正文);
}
使某人返回字符串();
}
}
}

FileStream类具有读取和写入流的异步方法:
ReadAsync
WriteAsync
,因此您只需在代码中交换这些方法,并在它们前面加上wait:

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();
    }
}
我相信这两种方法可以进一步简化,但这应该可以让您开始使用异步方法

正如您所知,如果您试图使用一个没有异步方法的类,并且希望在单独的线程上执行该任务,您仍然可以以这种方式使用async/await:

private async Task<string> ReadText(string filePath)
{
    return await Task.Run(() =>
    {
        return File.ReadAllText("textfilepath.txt");
    });
}
private async Task ReadText(字符串文件路径)
{
返回等待任务。运行(()=>
{
返回File.ReadAllText(“textfilepath.txt”);
});
}

FileStream类具有读取和写入流的异步方法:
ReadAsync
WriteAsync
,因此您只需在代码中交换这些方法,并在它们前面加上wait:

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();
    }
}
我相信这两种方法可以进一步简化,但这应该可以让您开始使用异步方法

正如您所知,如果您试图使用一个没有异步方法的类,并且希望在单独的线程上执行该任务,您仍然可以以这种方式使用async/await:

private async Task<string> ReadText(string filePath)
{
    return await Task.Run(() =>
    {
        return File.ReadAllText("textfilepath.txt");
    });
}
private async Task ReadText(字符串文件路径)
{
返回等待任务。运行(()=>
{
返回File.ReadAllText(“textfilepath.txt”);
});
}

让我们一次只取一个:

public async Task WriteFile()
{
  string filePath = @"SampleFile.txt";
  string text = txtContents.Text;
  Task task1 = new Task( () =>  WriteTextAsync(filePath, text));
}
task1在这里做什么?您需要实际运行它并等待它:

public async Task WriteFile()
{
  string filePath = @"SampleFile.txt";
  string text = txtContents.Text;
  Task task1 = new Task( () =>  await WriteTextAsync(filePath, text));
  await task1;
}
但是等等!我们正在创建一个
任务
,它创建一个
任务
,然后等待该
任务
。为什么不首先返回
任务

public Task WriteFile()
{
  string filePath = @"SampleFile.txt";
  string text = txtContents.Text;
  return WriteTextAsync(filePath, text);
}
记住,
async
使我们更容易创建在
任务
中执行某些操作的方法,但是如果您已经有了
任务
,那就是浪费时间

另外,按照惯例,您应该将异步方法的名称命名为
Async
。这里更是如此,因为您与其他
WriteTextAsync
的区别仅在于签名:

public Task WriteTextAsync()
{
  return WriteTextAsync(@"SampleFile.txt", txtContents.Text);
}
实际上,这与使用非异步
void WriteText(string filePath,string text)
时如何从非异步
void WriteText()调用它没有什么不同。这里没有什么新鲜事

现在,我们来谈谈更复杂的
WriteTextAsync

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<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();
    }
  }
}
因为我们现在有了任务,我们根本不需要使用旧的
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();
  }
}
对于非异步版本,一个更好的整体解决方案是使用
ReadToEnd()
,该解决方案更简单、更具弹性,可以使用
ReadToEnd()
。同样,这里更好的版本是使用
ReadToEndAsync()

以及:

private async Task ReadText(字符串文件路径)
{
使用(FileStream sourceStream=newfilestream(filePath,
文件模式。打开,文件访问
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();
  }
}