Net4.5 的异步和线程(未完)

来源链接:

http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N?msg=5070746

异步编程和线程并发是非常重要的功能.

异步编程即可以使用线程,也可以不用,同样的,也可以与线程一起使用.我们最好先了解下这两个功能.

本文概括:

1,异步编程

2,何时需要使用线程

3,基于异步的Task

4,并行编程

5,结论

异步编程

异步指的是,独立于主线程之外的操作.

以一个C#控制台开发为例,通过Main 方法作为程序的入口,return 作为它的出口.代码是按顺序执行的,下一行代码必须等待上一行执行完毕,才能开始运行.

static void Main(string[] args)
        {
           DoTaskOne();
           DoTaskTwo();
        }

方法"DoTaskTwo"必须等待"DoTaskOne"完成后,才开始执行.换句话说,“DoTaskOne”阻挡了程序的执行,直到自己本身执行完成.

异步编程可以将一个方法声明为在后台执行,使用异步后,执行到指定方法时,其会调用一个线程来运行该方法.(我们将在稍 后详细讨论Thread和Task)

在我们的示例中,如果使用异步来调用"DoTaskOne",在执行时,会立即开始执行"DoTaskTwo",而不需要等待"DoTaskOne"完成.

We can create our own thread using Thread class or use asynchronous patterns provided by .NET to perform asynchronous programming. There are three different asynchronous patterns in .NET:

  1. Asynchronous Programming Model (APM) pattern
  2. Event-based Asynchronous Pattern (EAP)

Both the above models are not recommended by Microsoft so we will not discuss about them. If you are interested you can read more from following msdn link:

https://msdn.microsoft.com/en-us/library/ms228963(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/ms228969(v=vs.110).aspx

  1. Task-based Asynchronous Pattern (TAP): This model is recommended so we will discuss it in detail

Threading is required or not

If we use asynchronous programming pattern that .NET introduced in 4.5, in most of the cases we need not to create manual thread by us. The compiler does the difficult work that the developer used to do.

Creating a new tread is costly, it takes time. Unless we need to control a thread, then “Task-based Asynchronous Pattern (TAP)” and “Task Parallel Library (TPL)” is good enough for asynchronous and parallel programming. TAP and TPL uses Task (we will discuss what is Task latter). In general Task uses the thread from ThreadPool(A thread pool is a collection of threads already created and maintained by .NET framework. If we use Task, most of the cases we need not to use thread pool directly. Still if you want to know more about thread pool visit the link: https://msdn.microsoft.com/en-us/library/h4732ks0.aspx)

But Task can be run:

  1. In the current thread
  2. In a new thread
  3. In a thread from the thread pool
  4. Or even without any thread

But if we use Task, as a developer we need not to worry about creation or uses of the thread, .NET framework handles the inner difficulties for us.

Anyway if we need some control over the thread like,

  1. We want to set a name for the tread
  2. We want to set priority for the thread
  3. We want to make our thread foreground or background

Then we may have to create our own thread using thread class.

Creating Thread using Thread class

The constructor of Thread class accepts a delegate parameter of type

  1. ThreadStart: This delegate defines a method with a void return type and no parameter.
  2. And ParameterizedThreadStart: This delegate defines a method with a void return type and one object type parameter.

Following is the simple example how we can start a new thread with Start method:

Hide   Copy Code

static void Main(string[] args)
        {
            Thread thread = new Thread(DoTask);
            thread.Start();// Start DoTask method in a new thread
            //Do other tasks in main thread
        }
        static public void DoTask() {
            //do something in a new thread
        }

We can use lamda expression instead of named method:

Hide   Copy Code

static void Main(string[] args)
        {
            Thread thread = new Thread(() => {
                //do something in a new thread
            });

            thread.Start();// Start a new thread
            //Do other tasks in main thread
        }

If we don’t require the variable reference we can even start the thread directly like:

Hide   Copy Code

static void Main(string[] args)
        {
            new Thread(() => {
                //do something in a new thread
            }).Start();// Start a new thread

            //Do other tasks in main thread
        }

But if we want to control the tread object after it is created we require the variable reference. We can assign different values to the different properties of the object like:

Hide   Copy Code

static void Main(string[] args)
        {
            Thread thread = new Thread(DoTask);

            thread.Name = "My new thread";// Asigning name to the thread
            thread.IsBackground = false;// Made the thread forground
            thread.Priority = ThreadPriority.AboveNormal;// Setting thread priority
            thread.Start();// Start DoTask method in a new thread
            //Do other task in main thread
        }

With the reference variable we can perform some function like abort the thread or wait for the tread to complete by calling the join method. If we call join to a thread the main tread blocks until the calling thread completes.

If we want to pass some data to the method we can pass it as a parameter of Start method. As the method parameter is object type we need to cast it properly.

Hide   Copy Code

static void Main(string[] args)
        {
         Thread thread = new Thread(DoTaskWithParm);
         thread.Start("Passing string");// Start DoTaskWithParm method in a new thread
         //Do other task in main thread
        }
        static public void DoTaskWithParm(object data)
        {

            //we need to cast the data to appropriate object

        }

“async” and “await” keywords

.NET framework introduced two new keywords to perform asynchronous programing: “async” and “await”. To use “await” keyword within a method we need to declare the method with “async” modifier. “await” keyword is used before calling an asynchronous method. “await” keyword suspends further execution of the method and control is return to the calling thread. See the example:

Hide   Copy Code

private async static void CallerWithAsync()// async modifier is used
{
string result = await GetSomethingAsync();// await is used before a method call. It suspends //execution of CallerWithAsync() method and control returs to the calling thread that can //perform other task.

Console.WriteLine(result);// this line would not be executed before  GetSomethingAsync() //method completes
}

The “async” modifier can only be used with methods returning a Task or void. It cannot be used with the entry point of a program, the Main method.

We cannot use await keyword before all the methods. To use “await” the method must have to return “awaitable” type. Following are the types that are “awaitable”:

  1. Task
  2. Task<T>
  3. Custom “awaitable” type. Using a custom type is a rare and advanced scenario; we will not discuss it here.

Task-based Asynchronous Pattern

First of all we need an asynchronous method that returns Task or Task<T>. We can create Task by following ways:

  1. Task.Factory.StartNew method: Prior to .NET 4.5 (in .NET 4) this was the primary method to create and schedule a task.
  2. Task.Run or Task.Run<T> Method: From .NET 4.5 this method should be used. This method is sufficient for most of the common cases.
  3. Task.FromResult method: If the result is already computed, we can use this method to create a task.

Task.Factory.StartNew has still some important uses for advance scenario. Please see the link for more information: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

Following links shows some ways to create Task: http://dotnetcodr.com/2014/01/01/5-ways-to-start-a-task-in-net-c/

Creating and awaiting for a Task

We will create our Task using Task.Run<T> method. This method Queues the specified work to run on the ThreadPool and returns a task handle for that work. Following steps are needed to create an asynchronous Task from a synchronous method:

  1. Let’s assume that we have a method that is synchronous but take some time to complete:

Hide   Copy Code

       static string Greeting(string name)
        {
        Thread.Sleep(3000);

        return string.Format("Hello, {0}", name);

       }
  1. To access this method asynchronously we have to wrap it with an asynchronous method. Let’s assume the name is “GreetingAsync”. It is a convention to add “Async” suffix to the name of an asynchronous method.

Hide   Copy Code

                      static Task<string> GreetingAsync(string name)
                         {
                               return Task.Run<string>(() =>
                                     {
                                        return Greeting(name);
                                      });
                     }
  1. Now we can call the asynchronous method GreetingAsync by using the await keyword

Hide   Copy Code

                    private async static void CallWithAsync()
                        {
                             //some other tasks
                                string result = await GreetingAsync("Bulbul");
                                //We can add  multiple &ldquo;await&rdquo; in same &ldquo;async&rdquo; method
                               //string result1 = await GreetingAsync(&ldquo;Ahmed&rdquo;);
                              //string result2 = await GreetingAsync(&ldquo;Every Body&rdquo;);
                              Console.WriteLine(result);
                     }

When “CallWithAsync” method is called it starts executing like regular synchronous method until it reaches “await” keyword. When it reaches to the “await” keywords it poses execution for the method and start waiting for “GreetingAsync("Bulbul")” method to be finished. In the meantime the control returns to the caller of “CallWithAsync” method and the caller can do its other task as usual.

When “GreetingAsync("Bulbul")” method finishes, “CallWithAsync” method resumes its other task after “await” keywords. In this case it executes the code “Console.WriteLine(result)

  1. Continuation with Task: “ContinueWith” method of Task class defines the code that should be invoked as soon as the task is completed.

Hide   Copy Code

                           private static void CallWithContinuationTask()
                                 {
                                    Task<string> t1 = GreetingAsync("Bulbul");
                                   t1.ContinueWith(t =>

                                        {

                                             string result = t.Result;

                                             Console.WriteLine(result);
                                       });
                               }

We need not to use “await” keyword if we use “ContinueWith” method, compiler will put the “await” keyword into appropriate place.

Awaiting for multiple asynchronous methods

Let us look at the following code:

Hide   Copy Code

                   private async static void CallWithAsync()
                        {
                             string result = await GreetingAsync("Bulbul");
                             string result1 = await GreetingAsync(&ldquo;Ahmed&rdquo;);
                             Console.WriteLine(result);
                            Console.WriteLine(result1);
                   }

Here we are awaiting for the two calling sequentially. Second call of the “GreetingAsync(“Ahmed”)” will be started after finishing the first call “GreetingAsync("Bulbul")”. If “result” and “result1” of the above code are not dependent, then the sequential “awiting” is not a good practice.

In that case we can simply call the methods without “await” keywords and “awaits” for them in a single place by combinators. In that case both the method call can be executed in parallel.

Hide   Copy Code

                      private async static void MultipleAsyncMethodsWithCombinators()
                        {

                           Task<string> t1 = GreetingAsync("Bulbul");

                           Task<string> t2 = GreetingAsync("Ahmed");

                           await Task.WhenAll(t1, t2);

                         Console.WriteLine("Finished both methods.\n " +

                        "Result 1: {0}\n Result 2: {1}", t1.Result, t2.Result);
                      }

Here we use Task.WhenAll combinator. Task.WhenAll creates a task that will complete when all of the supplied tasks have completed. Task class has another combinator. Task.WhenAny, that will complete when any of the supplied tasks have completed.

Handling Exceptions

We have to put “await” code blocks inside a try block to handle the exception of the method.

Hide   Copy Code

                   private async static void CallWithAsync()
                      {
                          try
                          {
                              string result = await GreetingAsync("Bulbul");
                          }
                        catch (Exception ex)
                         {
                        Console.WriteLine(&ldquo;handled {0}&rdquo;, ex.Message);
                        }
             }

If we have multiple “await” inside the try block only the first exception will be handled and the next “await” would not be reached. If we want all the methods to be called even some one throw exception we have to call them without “await” keyword and wait for all the tasks using Task.WhenAll method.

Hide   Copy Code

        private async static void CallWithAsync()
              {
                 try
                   {

                       Task<string> t1 = GreetingAsync("Bulbul");

                      Task<string> t2 = GreetingAsync("Ahmed");

                      await Task.WhenAll(t1, t2);
                  }
               catch (Exception ex)
               {
               Console.WriteLine(&ldquo;handled {0}&rdquo;, ex.Message);
             }
       }

Although all tasks will be completed, we can see exception only from first task. It’s not the task that threw the exception first, but the first task in the list.

One way to get the error from all the tasks is to declare them outside the try block so that the can be accessed from exception block and then check the “IsFaulted” property of the task. If it has an exception then the “IsFaulted” property will be true. And then we can get the exception by the inner exception of the task instances.

But there is another better way like this:

Hide   Copy Code

        static async void ShowAggregatedException()
           {
              Task taskResult = null;
              try
             {
                  Task<string> t1 = GreetingAsync("Bulbul");
                  Task<string> t2 = GreetingAsync("Ahmed");
                  await (taskResult = Task.WhenAll(t1, t2));
           }
          catch (Exception ex)
          {
             Console.WriteLine("handled {0}", ex.Message);
             foreach (var innerEx in taskResult.Exception.InnerExceptions)
            {
               Console.WriteLine("inner exception {0}", nnerEx.Message);
           }
        }
  }

Canceling the Task

Previously if we used thread from ThreadPool, it was not possible to cancel the thread. Now Task class provides a way to cancel the started task based on the CancellationTokenSource class, Steps to cancel a task:

  1. The asynchronous method should except a parameter of type “CancellationToken
  2. Create an instance of CancellationTokenSource class like:

Hide   Copy Code

                    var cts = new CancellationTokenSource();
  1. Pass the CancellationToken from the instace to the asynchronous method, like:

Hide   Copy Code

                   Task<string> t1 = GreetingAsync("Bulbul", cts.Token);
  1. From the long running method, we have to call ThrowIfCancellationRequested() method ofCancellationToken.

Hide   Copy Code

                  static string Greeting(string name, CancellationToken token)
                    {
                       Thread.Sleep(3000);
                       token. ThrowIfCancellationRequested();
                      return string.Format("Hello, {0}", name);
                 }
  1. Catch the OperationCanceledException from where we are awiting for the Task.
  2. Now if we cancel the operation by calling Cancel method of instance of CancellationTokenSource, OperationCanceledException will be thrown from the long running operation. We can set time to cancel the operation to the instanc also. For more detail about CancellationTokenSource class please see following link:https://msdn.microsoft.com/en-us/library/system.threading.cancellationtokensource%28v=vs.110%29.aspx

Let us see the whole things in an example code, in this example we are canceling the operation after one second:

Hide   Shrink    Copy Code

static void Main(string[] args)
        {
            CallWithAsync();
            Console.ReadKey();
        }

        async static void CallWithAsync()
        {
            try
            {
                CancellationTokenSource source = new CancellationTokenSource();
                source.CancelAfter(TimeSpan.FromSeconds(1));
                var t1 = await GreetingAsync("Bulbul", source.Token);
            }
            catch (OperationCanceledException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        static Task<string> GreetingAsync(string name, CancellationToken token)
        {
            return Task.Run<string>(() =>
            {
                return Greeting(name, token);
            });
        }

        static string Greeting(string name, CancellationToken token)
        {
            Thread.Sleep(3000);
            token.ThrowIfCancellationRequested();
            return string.Format("Hello, {0}", name);
        }

Parallel programming

.NET 4.5 and above has introduced a class called “Parallel”, an abstraction over the thread class. Using the “Parallel” class we can implement parallelism. Parallelism differs from the Threading in a way that it uses all the available CPU or core. Two type of parallelism is possible,

  1. Data Parallelism: If we have a big collection of data and we want some operation on each of the data to perform parallely then we can use data parallelism. Parallel class has static For or ForEach method to perform data parallelism, like

Hide   Copy Code

             ParallelLoopResult result =
                    Parallel.For(0, 100, async (int i) =>
                    {
                        Console.WriteLine("{0}, task: {1}, thread: {2}", i,
                        Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                        await Task.Delay(10);

              });

Parallel For or ForEach may use several threads and the index (in the code i) is not sequential.

If we want to stop parallel For o ForEach method earlier we may pass ParallelLoopState as a parameter and based on the state we can break the loop.

Hide   Copy Code

        ParallelLoopResult result =
                    Parallel.For(0, 100, async (int i, ParallelLoopState pls) =>
                    {
                        Console.WriteLine("{0}, task: {1}, thread: {2}", i,
                        Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                        await Task.Delay(10);
                        if (i > 5) pls.Break();
              });

Be careful while breaking the loop, as it is running in several threads may run larger than the break point. We should not take any decision based on the break point.

  1. Task Parallelism: If we want to run multiple task in parallel we can use task parallelism by calling the invoke method of Parallel class. Parallel.Invoke method accepts an array of Action delegate. For example:

Hide   Copy Code

               static void ParallelInvoke()
                {
                    Parallel.Invoke(MethodOne, MethodTwo);

               }

Conclusion

I have tried to introduce asynchronous programming technique that .NET framework 4.5 provided. I have tried to keep the things simple and did not go into advanced detail. Many of the examples and references are taken from the "Professional C# 2012 and .NET 4.5 of Wrox".

时间: 2024-10-17 06:43:37

Net4.5 的异步和线程(未完)的相关文章

听风讲MVC丶 —— 一言不合就撸码 (未完待续&#183;&#183;&#183;&#183;&#183;&#183;)

     希望你看了此小随 可以实现自己的MVC框架     也祝所有的程序员身体健康一切安好                                                                                                                                                ——久伴深海丶默 1.什么是前端控制器(font controller).Java Web中的前端控制器是应用的门面,

whatweb.rb 未完待续

#!/usr/bin/env ruby #表示ruby的执行环境 =begin # ruby中用=begin来表示注释的开始 .$$$ $. .$$$ $. $$$$ $$. .$$$ $$$ .$$$$$$. .$$$$$$$$$$. $$$$ $$. .$$$$$$$. .$$$$$$. $ $$ $$$ $ $$ $$$ $ $$$$$$. $$$$$ $$$$$$ $ $$ $$$ $ $$ $$ $ $$$$$$. $ `$ $$$ $ `$ $$$ $ `$ $$$ $$' $ `$

《Linux内核分析》MOOC课程之从迷你Linux内核角度理解进程时间轮片调度(未完)

代码分析 mypcb.h mymain.c 上面这段代码主要完成了对0号进程的初始化,即pid置为0,状态state置为0(即runnable状态),进程入口及当前进程的线程的ip指向my_process,线程的sp指向当前进程的进程堆栈,由于目前只有0号进程,所以next指针指向自己形成一个单PCB链表. 上面这段代码主要是扩充循环链表,使用memcpy()复制0号进程的状态给创建的从1号到MAX_TASK_NUM-1号进程,并与0号进程一起构成一个循环PCB链表. 上面这段代码功能是从循环P

JavaWeb ajax编程(未完待续)

1.Ajax 1.1Ajax的定义 Ajax:(Asynchronous JavaScript And XML)指异步 JavaScript 及 XML. 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术,是基于JavaScript.XML.HTML.CSS新用法. Ajax:只刷新局部页面的技术 JavaScript:更新局部的网页 XML:一般用于请求数据和响应数据的封装 XMLHttpRequest对象:发送请求到服务器并获得返回结果 CSS:美化页面

性能测试调优篇---未完待续

性能测试调优一:1.首先,看下选测交易的整个走向纯系统内部交易:选测交易如果是系统内的交易,每一步请求都和系统交互几次,访问了几个数据库,访问了数据库的那几张表??该交易走了那几台机器,这几台机器的网络连接情况是什么样的??这几台机器是通过走的是哪些虚拟网卡,走了哪些路由器??带宽是什么情况??该交易在这几台机器上消耗了多少CPU,内存,及其对磁盘做了多少次的访问??从方法层面,从该交易的发起到结束,起了多少线程,调用了哪些相关的方法以及接口,访问了哪些表???跨系统交易:该交易发起后,每一步请

把握linux内核设计思想系列(未完待续......)

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 把握linux内核设计思想(一):系统调用 把握linux内核设计思想(二):硬中断及中断处理 把握linux内核设计思想(三):下半部机制之软中断 把握linux内核设计思想(四):下半部机制之tasklet 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择 把握linux内核设计思想(六):内核时钟中断 把握linux内核设计思想(七):内核定时器和

细菌觅食优化算法:理论基础,分析,以及应用(未完)

原作者:Swagatam Das,Arijit Biswas,Sambarta Dasgupta,和Ajith Abraham  [摘 要]细菌觅食优化算法(Bacterial foraging optimization algorithm[BFOA])已经被分布式优化和控制的同行们当作一种全局性的优化算法接受.BFOA是由大肠杆菌的群体觅食行为所启发而总结出来 的.BFOA已经吸引了足够多的研究者的注意,由于它出现在解决真实世界中一些应用领域上优化问题的高效性.E.coli 的群体策略的生物基

Linux设备驱动开发学习(2):Linux设备驱动简介(未完)

(未完待续......)

[译]App Framework 2.1 (1)之 Quickstart (未完待续)

最近有移动App项目,选择了 Hybrid 的框架Cordova  和  App Framework 框架开发. 本来应该从配置循序渐进开始写的,但由于上班时间太忙,这段时间抽不出空来,只能根据心情和兴趣,想到哪写到哪,前面的部分以后慢慢补上. App Framework 前生是是叫 jqMobi 注意大家不要和 jQuery Mobile 混淆了,它们是两个不同的框架,一开始我还真混淆了0.01秒. 这里我先翻译一下Quickstart 部分,一是自己工作上用的上,二是也想顺便练练英文,最关键