您的位置:首页 > 其它

前线解释多线程之委托《三》

2013-08-07 09:48 141 查看
委托之同步调用

1using System;

2using System.Threading;

3
4namespace 委托同步

5{
6class Program

7 {

8staticvoid Main(string[] args)

9 {
10 Console.WriteLine("故事场景:老板正在陪二奶吃饭,经理去汇报工作");
11//输出正在执行的线程的ID

12 Console.WriteLine("当前部门经理汇报工作的酒店客房Id为:{0}",Thread.CurrentThread.ManagedThreadId);
13 Console.WriteLine("在同步模式下委托秘书去Report");
14 Secretary secretary = new Secretary(Report);

15string reportContent =secretary.Invoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶");

16//这里注意:当秘书完成Report()后下面的内容才会执行
17 Console.WriteLine("终于等到秘书汇报玩工作");

18 Console.WriteLine("汇报的全部内容为:\n{0}", reportContent);

19 Console.ReadKey();
20 }

21staticstring Report(string formulae, string content)

22 {

23 Console.WriteLine("当前秘书代为汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);

24 Console.WriteLine("模拟:老板正在谈给二奶转正的事情,此时间段内秘书也给等着,等了五个小时%^$$#&*");

25 Thread.Sleep(5000);

26return formulae+":"+content;
27 }

28 }

29publicdelegatestring Secretary(string formulae, string content);

30 }

我们先来看看执行效果再说,当然(其中耗时部分的)动态的效果需要你自己复制后执行以下才能看到。
好了,玩笑归玩笑,我们还是看程序的主体,而不要看汉字,嘿嘿,认真看汉字的一定不单纯。
总结一下:

我们从Thread.CurrentThread.ManagedThreadId获取到的线程Id看到,前后执行的任务都是在同一个线程中进行的,而且当我们执行string reportContent =secretary.Invoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶");的时候,发现在这个耗时操作中,后续的程序将停顿执行,知道该任务完成才继续执行后续代码。这样就达到了同步(阻塞)的效果。同时对前面【Invoke()方法只不过是用来绑定委托调用对象参数的】做个简单演示

1string reportContent =secretary.Invoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶");
2//======等效于===============//
3string reportContent = secretary("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶");

这时候我们觉悟了,他也就只不过是用同一个线程去干几个不同的任务。但是不能一心二用,干一件事,就得放下另一件。
所以以后要是看见别人用Invoke()并且坚持说是执行他所谓的异步(或者两个线程)你就可以可以认为他在胡说,当然观棋不语真君子,自己明白就行了,这个社会,就这样啦,你懂得。

委托之异步调用

这个时候我们应该想前面Report方法执行的那段时间我们可以不可以同时执行别的方法,答案是肯定的,这里就引出了异步调用的概念,等到我们搞定它了再来对比他和委托同步调用的区别。继续今天的追本溯源:
我们先来看一下委托的原始定义:

当然,既然是处理委托肯定是要和delegate关键字打交道,那么我们来追忆一下上面的程序中的一个关键部位,别乱想。

public delegate string Secretary(string formulae, string content);
那么编译的时候,编译器会做什么处理呢,当然你要是追踪的话可不要追踪到Delegate类的源头,那样只会一头雾水。

现在我们看一下编译后编译器做了哪些处理。

于是,我们知道了编译器在处理delegate关键字的时候动态的生成了BeginInvoke()和EndInvoke()方法,我们继续查看一下他们的原型:

1 [MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]

2publicvirtual IAsyncResult BeginInvoke(string formulae, string content, AsyncCallback callback, object @object);

3 [MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
4publicvirtualstring EndInvoke(IAsyncResult result);
我们看到BeginInvoke()方法返回的一个IAsyncResult类型对象,我们转到定义看下提示,IAsyncResult是干什么的
提示是:表示异步操作的状态。MSDN说的很明白,就是最终返回实现的是AsyncResult类,同时查看定义也知道了
EndInvoke()方式并不是必须的,只是用了接受BeginInvoke()方法的返回值,当没有返回值的时候就没必要使用。
说了这么多废话,最重要的地方就是定义中提到的"异步两个字”,所以我们知道可以利用BeginInvoke()实现异步操作。

对于BeginInvoke()后面两个参数默认都是null,他们是用来干什么的呢?继续...

先修改下前面的程序,看看差别:

1using System;
2using System.Threading;
3

4namespace 委托同步
5{
6class Program

7 {

8staticvoid Main(string[] args)

9 {
10 Console.WriteLine("故事场景:老板正在陪二奶吃饭,经理去汇报工作");

11//输出正在执行的线程的ID

12 Console.WriteLine("当前部门经理汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);

13 Console.WriteLine("在同步模式下委托秘书去Report");
14 Secretary secretary = new Secretary(Report);
15 IAsyncResult AsyncResult = secretary.BeginInvoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶",null,null);
16 Console.WriteLine("小蜜去汇报吧,我去洗我的桑拿浴!");17//这里注意:当秘书完成Report()后下面的内容才会执行

18 Console.WriteLine("终于等到秘书汇报玩工作");

19string reportConten=secretary.EndInvoke(AsyncResult);

20 Console.WriteLine("汇报的全部内容为:\n{0}", reportConten);

21 Console.ReadKey();

22 }

23staticstring Report(string formulae, string content)

24 {

25 Console.WriteLine("当前秘书代为汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);
26 Console.WriteLine("模拟:老板正在谈给二奶转正的事情,此时间段内秘书也给等着,等了五个小时%^$$#&*");

27 Thread.Sleep(5000);
28return formulae + ":" + content;
29 }

30 }
31publicdelegatestring Secretary(string formulae, string content);

32 }

运行效果如下:
咋一看貌似,差不多,但是有两个明显不同的特征就是:
第一:前后线程Id不一样,一个是10,一个是9

第二: Console.WriteLine("小蜜去汇报吧,我去洗我的桑拿浴!");该语句不是等到

IAsyncResult AsyncResult = secretary.BeginInvoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶",null,null); 这句执行完了才执行的。

那么它的执行逻辑是:部门经理告诉老板的小蜜你替我汇报,我先去洗桑拿了,待会回来接受你返回的信息(返回主线程,用Console.WriteLine()方法打印出返回消息。)

总结:

  上面的确实现了异步,但是你有木有发现,这锅?

string reportConten=secretary.EndInvoke(AsyncResult);
Console.WriteLine("汇报的全部内容为:\n{0}", reportConten);
也就是说,并不是让小蜜去汇报,就和部门经理这条线没关系了,当我们创建了次线程去执行汇报,主线程任然实时监控
小蜜返回的信息。加入这段时间内部门经理洗桑拿回来了,但是小蜜还在汇报,于是急的不时打打电话问问小蜜情况咋样啦
前面提到了,IAsyncResult:表示异步操作的状态。于是它有必要提供了一个状态检测方法,结果看定义果然有滴
(IsCompleted);
于是继续这样修改下代码:

1using System;
2using System.Threading;
3
4namespace 委托同步

5{

6class Program

7 {

8staticvoid Main(string[] args)

9 {
10 Console.WriteLine("故事场景:老板正在陪二奶吃饭,经理去汇报工作");
11//输出正在执行的线程的ID

12 Console.WriteLine("当前部门经理汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);
13 Console.WriteLine("在同步模式下委托秘书去Report");
14 Secretary secretary = new Secretary(Report);
15 IAsyncResult AsyncResult = secretary.BeginInvoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶",null,null);16while (!AsyncResult.IsCompleted)
17 {

18 Console.WriteLine("还没汇报完,继续洗我的桑拿浴!");

19 }

20//这里注意:当秘书完成Report()后下面的内容才会执行

21 Console.WriteLine("终于等到秘书汇报玩工作");
22string reportConten=secretary.EndInvoke(AsyncResult);

23 Console.WriteLine("汇报的全部内容为:\n{0}", reportConten);
24 Console.ReadKey();

25 }
26staticstring Report(string formulae, string content)

27 {

28 Console.WriteLine("当前秘书代为汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);
29 Console.WriteLine("模拟:老板正在谈给二奶转正的事情,此时间段内秘书也给等着,等了五个小时%^$$#&*");
30 Thread.Sleep(5000);

31return formulae + ":" + content;

32 }

33 }

34publicdelegatestring Secretary(string formulae, string content);

35 }

我们再截取部分效果图看看运行效果:

结果,不是部门经理疯了就是小蜜疯了吧,一个实时询问,一个适时返回进展情况,尼玛是洗澡还是小蜜是复读机呀。
于是部门经理考虑到小蜜会烦,于是间隔一个小时问一次:代码如下修改局部:
while (!AsyncResult.IsCompleted)

{

Console.WriteLine("还没汇报完,继续洗我的桑拿浴!");

Thread.Sleep(1000);

}
运行效果如下:

于是,洗澡,正事两不误,而且小蜜也觉得经理这人实在。
当然当你觉得这中方式,最好的时候,其实IAsyncResult有提供了一个更加灵活的熟悉AsyncWaitHandle,等到了
获取到用于等待异步操作完成的 WaitHandle的时候,使用WaitOne方法可以更加的灵活的实现这个轮询机制,这个可以自己看下定义,这里就不再多说了,他的作用简答的说就是设置有效时间,超时就返回false。直接看修改的代码:

while (!AsyncResult.AsyncWaitHandle.WaitOne(1000,true))
{

Console.WriteLine("还没汇报完,继续洗我的桑拿浴!");

}
运行效果和上面一样:

或许你体验不到他的灵活我们试试,他返回false的情况:

while (!AsyncResult.AsyncWaitHandle.WaitOne(6000,true))

{

Console.WriteLine("还没汇报完,继续洗我的桑拿浴!");

}

Console.WriteLine("你洗澡好长啊,人家老板和小蜜都走了,你也回家吃饭去吧。");

轻轻一修改,比上面就明显灵活了
上面的几种方法,其实我们都在使用同样的方式进行线程的监控-----那就是"轮询机制",这样是不是感觉很累,于是,beginInvoke()的第三个参数开始出马了----AsyncCallback,一猜就知道是回调函数或者委托一类的东东,我们
来看看官方定义:引用在相应异步操作完成时调用的方法。这就是说,他是在次线程完成以后主动同时调用线程的一种方法
于是,自从有了AsyncCallback经理再也不发愁了,澡也洗好了,而且威望也提升了(引用某广告语,哈哈,学广告的把广告词忘了),我们这样修改下代码:

1using System;
2using System.Threading;

3using System.Runtime.Remoting.Messaging;

4

5namespace 委托同步

6{

7class Program

8 {

9staticvoid Main(string[] args)

10 {

11 Console.WriteLine("故事场景:老板正在陪二奶吃饭,经理去汇报工作");

12//输出正在执行的线程的ID

13 Console.WriteLine("当前部门经理汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);
14 Console.WriteLine("在同步模式下委托秘书去Report");

15 Secretary secretary = new Secretary(Report);

16 IAsyncResult AsyncResult = secretary.BeginInvoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶", new AsyncCallback(syncCallback), null);
17 Console.WriteLine("经理去干自己的事");

18 Console.ReadKey();

19 }
20staticstring Report(string formulae, string content)
21 {

22 Console.WriteLine("模拟:老板正在谈给二奶转正的事情,此时间段内秘书也给等着,等了五个小时%^$$#&*");

23 Thread.Sleep(5000);
24return formulae + ":" + content;
25 }
26staticvoid syncCallback(IAsyncResult ar)

27 {

28 Console.WriteLine("当前秘书代为汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);
29 Console.WriteLine("汇报工作已经完成");

30 AsyncResult asyncResult = (AsyncResult)ar;

31 Secretary secretary = (Secretary)asyncResult.AsyncDelegate;

32 Console.WriteLine("汇报的全部内容为:\n{0}", secretary.EndInvoke(ar));

33 }
34 }

35publicdelegatestring Secretary(string formulae, string content);

36 }

运行效果如下:
总结:

这样做有什么好处呢,第一,不用因为轮询而造成线程局部资源的浪费,经理不用自己去问,完成报告,移动短信就发给他了。有些人或许疑问,那么第四个参数是干什么的,这个其实就是添加附带一些自定义的额外意义上有用的信息(Object类型的),随便举个小例子。

1using System;

2using System.Threading;

3using System.Runtime.Remoting.Messaging;

4

5namespace 委托同步

6{

7class Program

8 {

9staticvoid Main(string[] args)
10 {
11 Console.WriteLine("故事场景:老板正在陪二奶吃饭,经理去汇报工作");
12//输出正在执行的线程的ID

13 Console.WriteLine("当前部门经理汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);

14 Console.WriteLine("在同步模式下委托秘书去Report");

15 Secretary secretary = new Secretary(Report);
16 IAsyncResult AsyncResult = secretary.BeginInvoke("亲爱的Boss", "我们公司今年业绩不错,XX也有了二奶", new AsyncCallback(syncCallback), "2012.06.26,本故事由 http://www.cnblogs.com/rohelm 胡编乱造");

17 Console.WriteLine("经理去干自己的事");
18 Console.ReadKey();

19 }
20staticstring Report(string formulae, string content)

21 {
22 Console.WriteLine("模拟:老板正在谈给二奶转正的事情,此时间段内秘书也给等着,等了五个小时%^$$#&*");
23 Thread.Sleep(5000);

24return formulae + ":" + content;
25 }
26staticvoid syncCallback(IAsyncResult ar)

27 {

28 Console.WriteLine("当前秘书代为汇报工作的酒店客房Id为:{0}", Thread.CurrentThread.ManagedThreadId);

29 Console.WriteLine("汇报工作已经完成");

30 AsyncResult asyncResult = (AsyncResult)ar;

31 Secretary secretary = (Secretary)asyncResult.AsyncDelegate;

32string Info=(string)asyncResult.AsyncState;

33 Console.WriteLine("汇报的全部内容为:\n{0}", secretary.EndInvoke(ar));

34 Console.WriteLine(Info);

35 }
36 }
37publicdelegatestring Secretary(string formulae, string content);

38 }

运行效果:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  故事 多线程 经理