After all, the callback is invoked by .NET using your delegate, but still it is .NET that calls this delegate. It is your right and duty to know on which thread your code is executed on. To give a clear picture of what is going on, I decided to yet again modify my Foo
function to include thread information and add a delay of 4 seconds.
等下!线程是不是回调了?
毕竟,使用了委托来回调的。.NET调用这个委托。你有权知道你的代码执行了那个线程。为了清楚描述,我决定再修改Foo函数,包括线程信息和增加4秒延迟。
private string FooWithOutAndRefParameters(string param1, out int param2, ref ArrayList list) { // log thread information Trace.WriteLine("In FooWithOutAndRefParameters: Thread Pool? " + Thread.CurrentThread.IsThreadPoolThread.ToString() + " Thread Id: " + Thread.CurrentThread.GetHashCode()); // wait for 4 seconds as if this functions takes a while to run. Thread.Sleep(4000); // lets modify the data! param1 = "Modify Value for param1"; param2 = 200; list = new ArrayList(); return "Thank you for reading this article"; }
I also added thread information to the callback function:
我在回调函数里也增加线程信息
private void CallBack(IAsyncResult ar) { // which thread are we on? Trace.WriteLine("In Callback: Thread Pool? " + Thread.CurrentThread.IsThreadPoolThread.ToString() + " Thread Id: " + Thread.CurrentThread.GetHashCode()); // define the output parameter int intOutputValue; ArrayList list = null; // first case IAsyncResult to an AsyncResult object, // so we can get the delegate that was used to call the function. AsyncResult result = (AsyncResult)ar; // grab the delegate DelegateWithOutAndRefParameters del = (DelegateWithOutAndRefParameters) result.AsyncDelegate; // now that we have the delegate, we must call EndInvoke on it, so we // can get all the information about our method call. string strReturnValue = del.EndInvoke(out intOutputValue, ref list, ar); }
I decided to execute FooWithOutAndRefParameters
multiple times, using a button on my form.
我决定执行多次执行 FooWithOutAndRefParameters ,在我的界面上使用一个按钮
private void button4_Click(object sender, EventArgs e) { CallFooWithOutAndRefParametersWithCallback(); }
Let�s see the output after pressing my button thrice (calling the function thrice):
按动按钮3次,看看输出什么(调用函数3次):
In FooWithOutAndRefParameters: Thread Pool? True Thread Id: 7 In FooWithOutAndRefParameters: Thread Pool? True Thread Id: 12 In FooWithOutAndRefParameters: Thread Pool? True Thread Id: 13 In Callback: Thread Pool? True Thread Id: 7 In Callback: Thread Pool? True Thread Id: 12 In Callback: Thread Pool? True Thread Id: 13
Notice that my Foo
function is executed thrice, one after the other, on three separate threads. All the threads are on the thread pool. Notice also that the callback is also executed thrice, respectively, and they are all on the thread pool too. What makes this interesting is that, the callback seems to be executed on the same thread ID as Foo
. Thread 7 executes Foo
; 4 seconds later, the callback is also executed on thread 7. The same with thread 12 and 13. It is like the callback is a continuation of my Foo
function. I pressed my button many times, trying to see if the callback will ever be called on a thread Id other then the one Foo
is executed on, I was not able to achieve that. If you think about it, it makes total sense. Imagine, .NET would grab a thread to call Foo
and then grab another thread to call the callback, that would be a waste! Not to mention that if your thread pool is starved, you will end up waiting for a free thread just to call the callback! That would have been a disaster.
Foo函数执行了3次,一个连着一个,3个都是独立线程。所有线程都在线程池里。也能知道,回调也执行了3次,独立线程,也都在一个线程池中。是不是很有趣,回调函数和Foo函数线程ID是一致的。线程7执行了Foo,4秒后,回调也是线程7。线程12,线程13也是这样的。回调函数就是Foo函数的延续。我按了多次按钮,想知道回调函数和Foo函数线程ID会不会不一致,但是没有出现这样的情况。如果你仔细想想,也会觉得有道理的。想想,如果.NET在调用Foo使用一个线程,回调函数时候又用另外一个线程,这太浪费资源了!毫无疑问,如果你的线程池耗尽,你就要等待一个释放了的线程,去调用回调函数。这将是一个灾难性的错误.