Asynchronous Method Invocation 【翻译】 (一)

来源:互联网 发布:汇通易贷 知乎 编辑:程序博客网 时间:2024/04/30 19:20

Introduction

In this article, I am going to explain asynchronous method calls and how to use them. After playing with delegates, threads, and asynchronous invocation for so long, it would be a sin not to share some of my wisdom and knowledge on the subject, so hopefully, you won�t be looking at an MSDN article at 1 AM wondering why you decided to go into computers. I will try to use baby steps and lots of examples� Overall, I will cover how to call methods asynchronously, how to pass parameters to such methods, and how to find out when a method completes execution. Finally, I will show the Command Pattern in use for simplifying some of the code. The big advantage with .NET asynchronous method invocation is that you can take any method you have in your project, and you can call it asynchronously without touching the code of your method. Although most of the magic is within the .NET framework, it is important to see what is going on in the back, and that�s what we are going to study here.

 

在这篇文章里,我将要说明的是什么是异步方法回调,怎么使用异步回调。写了那么长时间的代码,什么委托啊,线程啊,异步调用啊,经常的用这些玩意,如果哥哥我不把偶的天才总结智慧结晶和各位分享下那就是偶的罪过咯。各位干嘛要进入计算机行业那,看MSDN到凌晨一点,累死人咯。我将手把手用大量代码列子,告诉大家怎么使用异步方法,怎么用它传参,还有当方法执行时候的一些状况。

最后,我会写一些指令,简化一些代码。.net最牛B的地方就是异步调用,你可以在你的工程代码里随意使用它们,异步回调不用修改你写的方法的代码。虽然.net把一些东东写在内部,但是那,了解微软在.net内部到底干了些什么是很重要的。这也是我们在这里学习的原因。

 

Synchronous vs. Asynchronous

Let me try to explain synchronous and asynchronous method invocations with an example, because I know people on The Code Project like to see code and not read War and Peace (not that I have anything against this book).

 
让我们用个列子来解释下什么是同步,因为偶知道,大家在codeproject里一定喜欢看代码,而不是扯淡什么《战争与和平》(顺便说一句,我对这本书没什么意见)

 

 

Normally, when your application calls the functionFoo(), it will need to wait 10 seconds untilFoo() is finished and control is returned to the calling thread. Now, suppose you want to callFoo() 100 times, then we know that it would take 1000 seconds for the control to return to the calling thread. This type of a method invocation is Synchronous.

  1. Call Foo()
  2. Foo() is executed
  3. Control goes back to the calling thread

通常,当你的程序调用一个Foo()函数时,需要等待10秒钟,直到Foo()执行完毕,控制权才会返回到调用线程上。现在,假如你想调用Foo()函数100次,那么你要等1000秒才能返回原线程。这种方式是同步调用。

   1. 调用Foo()

   2. Foo()执行

   3. 控制权返回到调用的原线程

 

Let's now call Foo() using delegates because most of the work we will do here is based on delegates. Luckily for us, there is already a delegate within the .NET framework that allows us to call a function that takes no parameter and has no return value. The delegate is called MethodeInvoker. Let's play with it a little.

 

现在让我们用委托来调用Foo(),我们大部分工作中都是基于委托的。对我们来说幸运的是,.Net允许我们用无参数,无返回值的委托。这个委托就是MethodeInvoker。来我们简单使用下它。

Even with the example above, we are still calling Foo() synchronously. The calling thread still needs to wait for the Invoke() function to complete until the control is returned to the calling thread.

 

上面的例子我们同步调用了Foo()。调用它的线程需要等待Invoke()完成,才能回到自己原来的控制线程

Asynchronous method invocation

But what if I wanted to call Foo() and not wait for it to finish executing? In fact, to make things interesting, what if I didn�t care when it is finished? Let�s say, I just wanted to callFoo 100 times without waiting for any of the function calls to complete. Basically, doing something calledFire and Forget. You call the function, you don�t wait for it, and you just forget about it. And� let�s not forget! I am not willing to change a line of code in my super complicated fancyFoo() function.

 

异步调用

但是,如果我们想调用Foo()并且不必等待它执行完毕,该怎么做那?实际上,如果我们不去关心它什么时候结束,事情会变的更加有趣。也就是说,我要调用Foo()100次,而且不必等它完成这100次操作。基本上是我们调用完以后就不用去管它了。嗯,就是不管它了。而且那,我是不愿意修改我已经写好的,那么完美的Foo()函数了。

 

Let me make a few comments about the code above.

 

让我为上面的代码做一些小结。

  • Notice that BeginInvoke() is the line of code that executes theFoo() function. However, the control is returned to the caller right away, without waiting forFoo() to complete.
  • The code above does not know when a call to Foo() completes, I will cover that later.
  • BeginInvoke() is used instead of Invoke(). For now, don�t worry about the parameters this function takes; I will cover that in more detail later.

* 注意代码里那个BeginInvoke(),用来执行Foo()函数的。然而,不用等待Foo()执行完毕,控制权马上就好返回原来的调用者那了。

* 上面的代码不知道Foo()函数什么时候能执行完毕,关于这点我在后面会做一定的解释说明

* BeginInvoke()代替了Invoke().目前不要去关心参数问题,我们马上会做这方面更详细的解释的。

What is the magic that .NET is doing in the background

Once you ask the framework to call something asynchronously, it needs a thread to do the work. It can not be the current thread, because that would make the invocation synchronous (blocking). Instead, the runtime queues a request to execute the function on a thread from the .NET Thread Pool. You don�t really need to code anything for it, all of it happens in the background. But, just because it is all transparent doesn�t mean you should care about it. There are a few things to remember:

 

.NET在内部做了哪些操作那?

一旦你然framework去做异步调用,这就需要用到线程。这可不是当前线程,因为它是一个异步的回调(阻塞). 而且,运行时用了.net的线程池,并用队列请求的方式去执行功能函数。如果你没自己编写代码去做这些事情,.net的后台机制会自动完成这些操作。它们浅显,但并不意味着你应该去关心这些。还是有些事情需要注意和记住的:

  • Foo() is executed on a separate thread, a thread that belongs to the .NET Thread Pool.
  • A .NET Thread Pool normally has 25 threads in it (you can change that limit), and each timeFoo() is called, it is going to be executed on one of these threads. You can't control which one.
  • The Thread Pool has its limits! Once all the threads are used, an async method invocation is queued until one of the threads from the pool is freed. This is calledThread Pool Starvation, and normally when it comes to that, performance is compromised.

* Foo()的执行是在独立的线程上的,这个线程属于.NET的线程池。

* 一个.NET线程池一般可以存有25个线程(你可以修改线程个数),而且Foo()调用的时候,这个操作就在线程池里执行。你没有控制权。

* 线程池是有限制的。一旦所以线程都在使用,一个异步调用就要排队,直到有线程从线程池里释放。这叫做Thread Pool Starvation,

   这种情况程序运行时会受到影响的。

Don�t dive too deep into the thread pool, you might run out of oxygen!

不要过度使用线程池,否则你会挂的。

So, let's see an example of when the Thread Pool is starved. Let's modify our Foo function to wait for 30 seconds, and also let it report the following:

  • Number of avaible threads on the pool
  • If the thread is on the thread pool
  • The thread ID.

We know that initially the thread pool contains 25 threads, so I am going to call myFoo function asynchronously 30 times (to see what happens after the 25th call).

让我们用一个列子来演示线程池耗尽的情况。我们修改下Foo函数,等待时间设置成30秒,而且显示下面这些数据:

 

* 可利用的线程数

* 如果线程在线程池中

* 线程ID号

 

我们知道线程池最初默认是包含25个线程的,所以我们异步调用Foo函数30次(看看调用到25次以后会发生什么情况).

 

Output window:

输出:

 

Let�s make a few notes about the output:

  • Notice, first of all, that all the threads are on the thread pool.
  • Notice that each time Foo is called, another thread ID is assigned. However, you can see that some of the threads are recycled.
  • After calling Foo() 25 times, you can see that there are no more free threads on the pool. At this point, the application �waits� for a free thread.
  • Once a thread is freed, the program grabs it right away, calling Foo(), and still there are 0 free threads on the pool. This continues to happen untilFoo() is called 30 times.

让我们对输出做个小结:

首先,所以线程都在线程池中

每次调用Foo线程ID都是被赋值了。然后,一些线程被回收了

*  第25次调用以后,线程池里就没有空闲线程了。这种情况下,程序等待空闲的线程出现。

*  一旦有线程被释放,程序会立刻获取,然后调用Foo(),所以这时候线程池里依然是0个空闲线程。这种情况一直持续到Foo()被调用30次

 

So right away, not doing anything too fancy, we can make a few comments about calling methods asynchronously.

  • Know that your code will run in a separate thread, so some thread safety issues may apply. This is a topic on its own, and I will not cover it here.
  • Remember that the pool has its limits. If you plan to call many functions asynchronously and if they take a long time to execute, Thread Pool Starvation might occur.

所以那,不要做太过分的操作哦。我们可以对异步调用做个小结。

明白你的代码是运行在独立的线程上的,线程的存在有自己安全因素。关于这个话题,我这里就不废话了

*  记住,线程池是有限制的。如果你计划调用很多异步操作而且操作还很消耗时间,线程池枯竭就的情况就会发生了

 

 

原创粉丝点击