1. 程式人生 > 實用技巧 >.net 基於任務的非同步程式設計

.net 基於任務的非同步程式設計

-------------------------------------------------------隱式建立和執行任務--------------------------------------------------------------

Parallel.Invoke 方法提供了一種簡便方式,可同時執行任意數量的任意語句。 只需為每個工作項傳入 Action 委託即可。 建立這些委託的最簡單方式是使用 lambda 表示式。 lambda 表示式可呼叫指定的方法,或提供內聯程式碼。 下面的示例演示一個基本的 Invoke 呼叫,該呼叫建立並啟動同時執行的兩個任務。 第一個任務由呼叫名為 DoSomeWork

的方法的 lambda 表示式表示,第二個任務由呼叫名為 DoSomeOtherWork 的方法的 lambda 表示式表示。

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());

namespace ParallelTasks
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
using System.Net; class ParallelInvoke { static void Main() { // Retrieve Goncharov's "Oblomov" from Gutenberg.org. string[] words = CreateWordArray(@"http://www.gutenberg.org/files/54700/54700-0.txt"); #region ParallelTasks // Perform three tasks in parallel on the source array
Parallel.Invoke(() => { Console.WriteLine("Begin first task..."); GetLongestWord(words); }, // close first Action () => { Console.WriteLine("Begin second task..."); GetMostCommonWords(words); }, //close second Action () => { Console.WriteLine("Begin third task..."); GetCountForWord(words, "sleep"); } //close third Action ); //close parallel.invoke Console.WriteLine("Returned from Parallel.Invoke"); #endregion Console.WriteLine("Press any key to exit"); Console.ReadKey(); } #region HelperMethods private static void GetCountForWord(string[] words, string term) { var findWord = from word in words where word.ToUpper().Contains(term.ToUpper()) select word; Console.WriteLine($@"Task 3 -- The word ""{term}"" occurs {findWord.Count()} times."); } private static void GetMostCommonWords(string[] words) { var frequencyOrder = from word in words where word.Length > 6 group word by word into g orderby g.Count() descending select g.Key; var commonWords = frequencyOrder.Take(10); StringBuilder sb = new StringBuilder(); sb.AppendLine("Task 2 -- The most common words are:"); foreach (var v in commonWords) { sb.AppendLine(" " + v); } Console.WriteLine(sb.ToString()); } private static string GetLongestWord(string[] words) { var longestWord = (from w in words orderby w.Length descending select w).First(); Console.WriteLine($"Task 1 -- The longest word is {longestWord}."); return longestWord; } // An http request performed synchronously for simplicity. static string[] CreateWordArray(string uri) { Console.WriteLine($"Retrieving from {uri}"); // Download a web page the easy way. string s = new WebClient().DownloadString(uri); // Separate string into an array of words, removing some common punctuation. return s.Split( new char[] { ' ', '\u000A', ',', '.', ';', ':', '-', '_', '/' }, StringSplitOptions.RemoveEmptyEntries); } #endregion } } // The example displays output like the following: // Retrieving from http://www.gutenberg.org/files/54700/54700-0.txt // Begin first task... // Begin second task... // Begin third task... // Task 2 -- The most common words are: // Oblomov // himself // Schtoltz // Gutenberg // Project // another // thought // Oblomov's // nothing // replied // // Task 1 -- The longest word is incomprehensible. // Task 3 -- The word "sleep" occurs 57 times. // Returned from Parallel.Invoke // Press any key to exit
View Code

-------------------------------------------------------顯式建立和執行任務--------------------------------------------------------------

不返回值的任務由 System.Threading.Tasks.Task 類表示。 返回值的任務由 System.Threading.Tasks.Task<TResult> 類表示,該類從 Task 繼承。 任務物件處理基礎結構詳細資訊,並提供可在任務的整個生存期內從呼叫執行緒訪問的方法和屬性。 例如,可以隨時訪問任務的 Status 屬性,以確定它是已開始執行、已完成執行、已取消還是引發了異常。 狀態由 TaskStatus 列舉表示。

在建立任務時,你賦予它一個使用者委託,該委託封裝該任務將執行的程式碼。 該委託可以表示為命名的委託、匿名方法或 lambda 表示式。 lambda 表示式可以包含對命名方法的呼叫,如下面的示例所示。 請注意,該示例包含對 Task.Wait 方法的呼叫,以確保任務在控制檯模式應用程式結束之前完成執行。

1.Task建構函式

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Create a task and supply a user delegate by using a lambda expression.
      Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
      // Start the task.
      taskA.Start();

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.",
                        Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
View Code


2.Task.Run啟動任務

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.",
                          Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
View Code

3.Task.Factory.StartNew

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;

        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i],
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine("{0:N1}", sum);
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum;
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0
View Code

-------------------------------------------------------建立任務延續--------------------------------------------------------------

使用 Task.ContinueWith 和 Task<TResult>.ContinueWith 方法,可以指定要在先行任務完成時啟動的任務。 延續任務的委託已傳遞了對先行任務的引用,因此它可以檢查先行任務的狀態,並通過檢索 Task<TResult>.Result 屬性的值將先行任務的輸出用作延續任務的輸入。
using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var getData = Task.Factory.StartNew(() => {
                                             Random rnd = new Random();
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;

                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } );
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2,
                                                                         x.Result.Item3);
                                                 } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
View Code
using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var displayData = Task.Factory.StartNew(() => {
                                                 Random rnd = new Random();
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;

                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ).
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,
                                                             x.Result.Item3);
                                     } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
View Code