通过微软官网看完异步编程后的总结:Async和Await

 

1:使用async和await异步编程

常用使用场景如下:

Web 访问 HttpClient SyndicationClient
使用文件 StreamWriterStreamReaderXmlReader StorageFile
使用图像   MediaCaptureBitmapEncoderBitmapDecoder
WCF 编程 同步和异步操作

方法名前加async编程异步方法,方法返回类型 task  task<TResult>  void,需要使用异步方法结果用await 等待异步返回的结果;没有用await异步方法会作为同步方法执行。

async和await不会创建新线程,也不会阻止当前线程,异步方法不会在自身线程上运行,异步设计原理就是非阻塞操作,await等待的时候呢,会把操作权返回给调用方。

调用async方法的时候会在后台去线程池里调用闲置的线程执行方法,await就会挂起等待当前异步方法返回结果。

 异步方法无法声明inref 或 out参数。

2:使用Task.WhenAll扩展异步方法

当有多个异步需要执行到时候,可以使用Task.WhenAll执行异步集合,当所有异步方法在里面执行完后,Task.WhenAll才会结束。

C#复制

// Create a query.  
IEnumerable<Task<int>> downloadTasksQuery =   
    from url in urlList select ProcessURL(url, client);  

// Use ToArray to execute the query and start the download tasks.  
Task<int>[] downloadTasks = downloadTasksQuery.ToArray();  
// Await the completion of all the running tasks.  int[] lengths = await Task.WhenAll(downloadTasks);  

3:使用Async和Await并行发起web请求

就是在使用await前,在启动任务和等待任务之间可以发起多次请求。其它任务都会以隐式的方式运行,不会创建新线程。

C#复制

private async Task CreateMultipleTasksAsync()  
{  
    // Declare an HttpClient object, and increase the buffer size. The  
    // default buffer size is 65,536.  
    HttpClient client =  
        new HttpClient() { MaxResponseContentBufferSize = 1000000 };  

    // Create and start the tasks. As each task finishes, DisplayResults   
    // displays its length.  
    Task<int> download1 =   
        ProcessURLAsync("http://msdn.microsoft.com", client);  
    Task<int> download2 =   
        ProcessURLAsync("http://msdn.microsoft.com/library/hh156528(VS.110).aspx", client);  
    Task<int> download3 =   
        ProcessURLAsync("http://msdn.microsoft.com/library/67w7t67f.aspx", client);  

    // Await each task.  
    int length1 = await download1;  
    int length2 = await download2;  
    int length3 = await download3;  

    int total = length1 + length2 + length3;  

    // Display the total count for the downloaded websites.  
    resultsTextBox.Text +=  
        string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);  
}  

4:异步控制流

用到关键字await异步就将控制器返回调用方。

C#复制

public partial class MainWindow : Window  
{  
    // . . .  
    private async void startButton_Click(object sender, RoutedEventArgs e)  
    {  
        // ONE  
        Task<int> getLengthTask = AccessTheWebAsync();  

        // FOUR  
        int contentLength = await getLengthTask;  

        // SIX  
        resultsTextBox.Text +=  
            String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);  
    }  

    async Task<int> AccessTheWebAsync()  
    {  
        // TWO  
        HttpClient client = new HttpClient();  
        Task<string> getStringTask =  
            client.GetStringAsync("http://msdn.microsoft.com");  

        // THREE                   
        string urlContents = await getStringTask;  

        // FIVE  
        return urlContents.Length;  
    }  
}  

5:微调异步应用程序

    1. 取消一个异步任务或者一组异步任务

        不想等待一个异步任务完成,可以通过设置来取消该任务。

取消单个和取消列表类似:

C#复制



        // ***Declare a System.Threading.CancellationTokenSource.  
        CancellationTokenSource cts;  

        // ***Instantiate the CancellationTokenSource.  
        cts = new CancellationTokenSource();  

        // ***Send a token to carry the message if cancellation is requested.  
        int contentLength = await AccessTheWebAsync(cts.Token);  
            
        cts.Cancel();  

    2. 在一段时间后取消异步任务

如果不希望等待操作结束,可使用 CancellationTokenSource.CancelAfter 方法在一段时间后取消异步操作。 此方法会计划取 消未在 CancelAfter 表达式指定的时间段内完成的任何关联任务。

C#复制


        CancellationTokenSource cts;  
       
        // Instantiate the CancellationTokenSource.  
        cts = new CancellationTokenSource();  

        // ***Set up the CancellationTokenSource to cancel after 2.5 seconds. (You  
        // can adjust the time.)  
        cts.CancelAfter(2500);  

        await AccessTheWebAsync(cts.Token);  
         

    3.在完成一个任务后取消其余任务

    通过结合使用 Task.WhenAny 方法和 CancellationToken,可在一个任务完成时取消所有剩余任务。 WhenAny 方法采用任务集合中的一个参数。 该方法启动所有任务,并返回单个任务。 当集合中任意任务完成时,完成单个任务。

C#复制


 // Declare a System.Threading.CancellationTokenSource.  
 CancellationTokenSource cts;  

 // Instantiate the CancellationTokenSource.  
 cts = new CancellationTokenSource();  

 await AccessTheWebAsync(cts.Token);  
           
 cts = null;  
      
 if (cts != null)  
 {  
    cts.Cancel();  
 }  
     
 // ***Create a query that, when executed, returns a collection of tasks.  
 IEnumerable<Task<int>> downloadTasksQuery =  
                from url in urlList select ProcessURLAsync(url, client, ct);  

 // ***Use ToArray to execute the query and start the download tasks.   
 Task<int>[] downloadTasks = downloadTasksQuery.ToArray();  

 // ***Call WhenAny and then await the result. The task that finishes   
 // first is assigned to firstFinishedTask.  
 Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);  

 // ***Cancel the rest of the downloads. You just want the first one.  
 cts.Cancel();  
  
 // ***Bundle the processing steps for a website into one async method.  
 async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct)  
 {  
            // GetAsync returns a Task<HttpResponseMessage>.   
            HttpResponseMessage response = await client.GetAsync(url, ct);  

            // Retrieve the website contents from the HttpResponseMessage.  
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();  

            return urlContents.Length;  
 }  

     4.  启动多个异步任务并在其完成时进行处理

 通过使用 Task.WhenAny,可以同时启动多个任务,并在它们完成时逐个对它们进行处理,而不是按照它们的启动顺序进行处理。

C#复制


        // Declare a System.Threading.CancellationTokenSource.  
        CancellationTokenSource cts;  

        // Instantiate the CancellationTokenSource.  
        cts = new CancellationTokenSource();  

        await AccessTheWebAsync(cts.Token);  
           

        cts = null;  
      
        if (cts != null)  
        {  
                cts.Cancel();  
        }  
    
      
        // ***Create a query that, when executed, returns a collection of tasks.  
        IEnumerable<Task<int>> downloadTasksQuery =  
                from url in urlList select ProcessURL(url, client, ct);  

        // ***Use ToList to execute the query and start the tasks.   
        List<Task<int>> downloadTasks = downloadTasksQuery.ToList();  

        // ***Add a loop to process the tasks one at a time until none remain.  
        while (downloadTasks.Count > 0)  
        {  
            // Identify the first task that completes.  
            Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);  

            // ***Remove the selected task from the list so that you don't  
            // process it more than once.  
            downloadTasks.Remove(firstFinishedTask);  

            // Await the completed task.  
            int length = await firstFinishedTask;  
                    
         }  
     

        async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)  
        {  
            // GetAsync returns a Task<HttpResponseMessage>.   
            HttpResponseMessage response = await client.GetAsync(url, ct);  

            // Retrieve the website contents from the HttpResponseMessage.  
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();  

            return urlContents.Length;  
        }  

6.  处理异步重入问题

三种方式: 

  1. 按钮点击后变为禁用;
  2. 二次进入马上取消先前执行的任务,执行当前任务;
  3. 进入等待队列,依次执行;

C#复制

    // ***Disable the Start button until the downloads are complete.   
    StartButton.IsEnabled = false;   

    try  
    {  
        await AccessTheWebAsync();  
    }  
    catch (Exception)  
    {  
        ResultsTextBox.Text += "\r\nDownloads failed.";  
    }  
    // ***Enable the Start button in case you want to run the program again.   
    finally  
    {  
        StartButton.IsEnabled = true;  
    }

C#复制

    // *** Declare a System.Threading.CancellationTokenSource.          CancellationTokenSource cts;  
    // This line is commented out to make the results clearer in the output.  
    //ResultsTextBox.Clear();  

    // *** If a download process is already underway, cancel it.  
    if (cts != null)  
    {  
        cts.Cancel();  
    }  

    // *** Now set cts to cancel the current process if the button is chosen again.  
    CancellationTokenSource newCTS = new CancellationTokenSource();  
    cts = newCTS;  

    try  
    {  
        // ***Send cts.Token to carry the message if there is a cancellation request.  
        await AccessTheWebAsync(cts.Token);  

    }  
    
    // *** When the process is complete, signal that another process can proceed.  
    if (cts == newCTS)  
        cts = null;  

C#复制


    // ***Declare the following variables where all methods can access them.   
    private Task pendingWork = null;     
    private char group = (char)('A' - 1);      // ***Verify that each group's results are displayed together, and that          // the groups display in order, by marking each group with a letter.          group = (char)(group + 1);          ResultsTextBox.Text += string.Format("\r\n\r\n#Starting group {0}.", group);          try          {                  // *** Pass the group value to AccessTheWebAsync.                  char finishedGroup = await AccessTheWebAsync(group);                  // The following line verifies a successful return from the download and                  // display procedures.                   ResultsTextBox.Text += string.Format("\r\n\r\n#Group {0} is complete.\r\n", finishedGroup);          }          catch (Exception)          {                  ResultsTextBox.Text += "\r\nDownloads failed.";          }  
    
    private async Task<char> AccessTheWebAsync(char grp)      {              HttpClient client = new HttpClient();      
        // Make a list of the web addresses to download.              List<string> urlList = SetUpURLList();      
        // ***Kick off the downloads. The application of ToArray activates all the download tasks.              Task<byte[]>[] getContentTasks = urlList.Select(url => client.GetByteArrayAsync(url)).ToArray();             // ***Call the method that awaits the downloads and displays the results.             // Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.              pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp);              ResultsTextBox.Text +=             string.Format("\r\n#Task assigned for group {0}. Download tasks are active.\r\n", grp);              // ***This task is complete when a group has finished downloading and displaying.              await pendingWork;              // You can do other work here or just return.              return grp;      }  

    private async Task FinishOneGroupAsync(List<string> urls, Task<byte[]>[] contentTasks, char grp)      {              // ***Wait for the previous group to finish displaying results.              if (pendingWork != null)             await pendingWork;              int total = 0;              // contentTasks is the array of Tasks that was created in AccessTheWebAsync.              for (int i = 0; i < contentTasks.Length; i++)              {                      // Await the download of a particular URL, and then display the URL and                      // its length.                      byte[] content = await contentTasks[i];                      DisplayResults(urls[i], content, i, grp);                      total += content.Length;              }              // Display the total count for all of the websites.              ResultsTextBox.Text += tring.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);      }  

7. 使用异步进行文件访问

可使用异步功能访问文件。 通过使用异步功能,你可以调用异步方法而无需使用回调,也不需要跨多个方法或 lambda 表达式来拆分代码。 若要使同步代码异步,只需调用异步方法而非同步方法,并向代码中添加几个关键字。

可能出于以下原因向文件访问调用中添加异步:

  • 异步使 UI 应用程序响应速度更快,因为启动该操作的 UI 线程可以执行其他操作。 如果 UI 线程必须执行耗时较长的代码(例如超过 50 毫秒),UI 可能会冻结,直到 I/O 完成,此时 UI 线程可以再次处理键盘和鼠标输入及其他事件。

  • 异步可减少对线程的需要,进而提高 ASP.NET 和其他基于服务器的应用程序的可伸缩性。 如果应用程序对每次响应都使用专用线程,同时处理 1000 个请求时,则需要 1000 个线程。 异步操作在等待期间通常不需要使用线程。 异步操作仅需在结束时短暂使用现有 I/O 完成线程。

  • 当前条件下,文件访问操作的延迟可能非常低,但以后可能大幅增加。 例如,文件可能会移动到覆盖全球的服务器。

  • 使用异步功能所增加的开销很小。

  • 异步任务可以轻松地并行运行。

C#复制

//写文件public async void ProcessWrite()  
{  
    string filePath = @"temp2.txt";  
    string text = "Hello World\r\n";  

    await WriteTextAsync(filePath, text);  
}  

private async Task WriteTextAsync(string filePath, string text)  
{  
    byte[] encodedText = Encoding.Unicode.GetBytes(text);  

    using (FileStream sourceStream = new FileStream(filePath,  
        FileMode.Append, FileAccess.Write, FileShare.None,  
        bufferSize: 4096, useAsync: true))  
    {  
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);  
    };  
}  
//读文件public async void ProcessRead()  {          string filePath = @"temp2.txt";          if (File.Exists(filePath) == false)          {                  Debug.WriteLine("file not found: " + filePath);          }          else          {              try              {                      string text = await ReadTextAsync(filePath);                      Debug.WriteLine(text);              }              catch (Exception ex)              {                      Debug.WriteLine(ex.Message);              }          }  }  
//并行IOprivate async Task<string> ReadTextAsync(string filePath)  {          using (FileStream sourceStream = new         FileStream(filePath, FileMode.Open, FileAccess.Read,         FileShare.Read,bufferSize: 4096, useAsync: true))          {                  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();          }  }
public async void ProcessWriteMult()  {          string folder = @"tempfolder\";          List<Task> tasks = new List<Task>();          List<FileStream> sourceStreams = new List<FileStream>();          try          {                  for (int index = 1; index <= 10; index++)                  {                          string text = "In file " + index.ToString() + "\r\n";                          string fileName = "thefile" + index.ToString("00") + ".txt";                          string filePath = folder + fileName;                          byte[] encodedText = Encoding.Unicode.GetBytes(text);                          FileStream sourceStream = new FileStream(filePath,                              FileMode.Append, FileAccess.Write, FileShare.None,                              bufferSize: 4096, useAsync: true);                          Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);                          sourceStreams.Add(sourceStream);                          tasks.Add(theTask);                  }                      await Task.WhenAll(tasks);          }          finally          {                  foreach (FileStream sourceStream in sourceStreams)                  {                          sourceStream.Close();                  }          }  }  

猜你喜欢

转载自blog.csdn.net/w1109390306/article/details/80524808