西西河

主题:【原创】茴香豆的茴的第五种写法 -- 漫谈线程和异步调用 -- Highway

共:💬30 🌺9 新:
分页树展主题 · 全看首页 上页
/ 2
下页 末页
  • 家园 【原创】茴香豆的茴的第五种写法 -- 漫谈线程和异步调用

    孔先生一己教导我们说“茴香豆的茴有四种写法”。很惭愧,我基本上都不知道。前些日子呢,又有外国某位老先生教育我们说“.NET函数的异步调用也有四种写法...”。我定睛一看,凝思一想,破颜微笑。不仅知道了该老兄在讲什么东西,更发现了茴香豆的茴的第五种写法。于是乎,对自己的敬佩感油然而生。。。点看全图

    外链图片需谨慎,可能会被源头改

    那位说了,什么是异步调用?

    Well,异步调用就是非同步调用。用洋文说就是Asynchronous Invocation,和同步调用只有一字母之差,同步调用唤作Synchronous Invocation.

    Damn, 你这说了和没说一样。同步异步到底是个啥玩意儿?

    Well, well,同步异步呢听着是比较抽象。那就让我们举一个小例子来说明一下吧,shall we?

    比如说你老人家每天上班,到了办公室,放下公文包,打开电脑,然后拎上暖壶上茶炉房打开水。打回开水之后呢,沏茶,看报纸,然后开始正式工作--上网灌水,和MM们聊天。

    你这样有条不紊一样一样的来,就是同步调用。看起来很简单是吧?但问题是如果你去了茶炉房水没有开,或是排队的人很多怎么办?由于同步调用是完成了一步才能干下一步,你打不回来开水,就没法沏茶,上网灌水,和MM们聊天的行动也就被耽搁了。打开水你可以看成是一个比较费时的函数/方法,你调用他,你就被“绊”住了。这件事不完,就什么也干不了。这个“绊”就是同步调用的一个问题,学名叫做Blocking。

    如果你不想被打开水这个函数“绊住”,那你就需要一种不同的机制来调用它,这就是我们今天要说的“异步调用”。它的情形大概是这样的 -- 你去了茶炉房,冲里面嚷嚷一嗓子“喂,小李子,我把暖壶放这儿了,一会儿水开了你给我打上,给我送办公室去”。话说完,你老人家一掉屁股就回去了,茶虽然不能喝上,但是你可以看报纸,上网灌水鬼混了,是不是?

    到这里,大家是不是看出来点儿名堂了,异步调用的好处是你没有被绊住,并且一个叫做“小李子”的家伙掺和进来了。想想看这个问题应该是很好理解的,你不想在茶炉房耽误工夫,那开水总的有人给你打才行是不是。毫不奇怪,在计算机里也是这样的,你(一个线程)不想被Block,但那个费时间的函数总要有线程来执行才能完成是不是。异步调用解放了当前线程,具体任务交给其他线程来幕后完成。

    看起来很简单,是吧?但问题是“小李子”会造成很多意想不到的问题,他把开水给你往办公室送,结果你正开门往外走,一下在撞在了一起,搞一个乌眼青。或者说小李子好心,替你沏茶。但他不懂你这宜兴泥壶的名堂,咣叽给你搞砸了,怎么办?

    在计算机世界,引入多线程问题就立刻变得复杂了。首先你要保证线程间的协调,不会造成冲突甚至是死磕(dead lock),更重要的是一个线程永远也不能touch别的线程创建的GUI Element。在Windows世界里,这个规则被定义为“Worker thread can never modify UI components which are created by Primary UI thread”。搞Java编程的人对这个规矩因该不会陌生,Java的Swing里也有同样的规矩。

    为什么会是这样子呢?

    嘿嘿,这可就是说来话长了。你最好去问Bill Gates先生。圣经十诫里有一条叫做“不要偷窥邻居的财物,不要垂涎别人的老婆!(Thou shalt not covet thy neighbor's goods, thou shalt not covet thy neighbor's wife)”。你的线程要遵守的就是这条戒律,切切!!!

    所以小李子把开水打好后,要放在门口,并知会你一声。你老人家出来亲自把暖壶拿回去,沏茶倒水,亲自主理。这个过程叫做Thread Marshaling.

    好了,背景知识介绍完了,现在就让我们看看这茴香豆的五种写法吧!我们假设要完成一个非常复杂费时的操作(具体内容你可以发挥想象)。在这里我用的是一个非常简单的累加计算。

    点看全图

    外链图片需谨慎,可能会被源头改

    1。手工创建线程法

    Thread thread = new Thread(具体函数);

    thread.start();

    2。使用系统的Thread Pool

    WaitCallback callBack = new WaitCallback(具体函数);

    ThreadPool.QueueUserWorkItem(callBack, 参数);

    3。使用Delegate的异步调用

    使用Delegate的BeginInvoke,EndInvoke

    4。使用Timer

    TimerCallback callBack = new TimerCallback(具体函数);

    System.Threading.Timer timer = new System.Threading.Timer(callBack,参数,0,-1);

    注意,以上这四种方法具体函数都是在新的Thread上执行的,如果要在界面上显示计算结果,要Marshal Thread.在.NET中,可以使用Control.Invoke(...)来实现。CLR会自动使用Primary UI Thread来更新GUI。比如在我们这个例子里,程序为:

    delegate void UpdateDele(long number);

    ...

    private void safeUpdateLabel(long result)

    {

    UpdateDele uDele = new UpdateDele(updateLabel);

    object[] objs = new object[] {result};

    labelResult.Invoke(uDele, objs);

    }

    private void updateLabel(long number)

    {

    labelResult.Text = "Result: " + number.ToString();

    }

    5。使用Background Worker thread。

    这是.NET 2.0的一个新的feature,目的是想简化多线程和异步调用的复杂程度。但实际上问题似乎更复杂了,看得见的好处就是不用你操心Thread Marshaling了。使用这个家伙,你需要

    a. 拖一个Component到你的Form上

    b. 实现他的DoWork和RunWorkerCompleted两个方法

    c. 调用他的RunWorkerAsync方法

    好了,到这里,茴香异步豆的五种写法就介绍完了。你是不是会好奇的问,茴香同步豆会有多少种写法啊?

    嘿嘿,这个问题就更复杂了。从最简单最基本的Static invocation到fully dynamic invocation,零零种种有七八种之多。其中Reflection, RuntimeMethodHandle以及dynamic method比较深奥,一般老百姓很少会遇到,这里且按下不表(其实是我也表述不清楚

    最后,临结束前给大家留两个思考题:

    1) 如果要异步调用一个方法,上面提到的哪一种你认为比较好呢?

    2)如果是用Java来烹饪茴香异步豆,你能想出来几种写法?

    关键词(Tags): #异步调用#NET#Java元宝推荐:四月一日,
分页树展主题 · 全看首页 上页
/ 2
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河