从UI体验下异步调用的四种方法

我下面的DEMO是从MSDN下摘抄过来的,可是MSDN是从控制台打印出来,
如果要比较直观地理解线程间的操作,从直观体验上觉得还是直接从UI方面感觉比较好
而下面主要是介绍那四处方法的使用,为什么用,怎么用,
因为除了第一种异步回调之外,其他的界面都卡在那里一动不动,但并不是说其他三种不好,只是他们还有别的地方要用而已

而里面红色的注释,我想已经能表达我想说的,而代码我还不知怎么像有的朋友那样贴上去好看点,可能比较难看,将就点喽

 //委托,其就是函数签名,从这里可以看出,参数与返回值是与函数一模一样的,
    //而委托主要也是用于回调同步异步,观察者模式,改天再好好回味一下观察者模式

    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
    //这里写上返回值只是为了好玩,经常写没返回值的委托,写写看,嘿嘿
    public delegate string InvokeMethodCaller(string s);
    class AsyncDemo
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("Test method begins.");
            for (int i = 0; i < callDuration / 1000; i++)
            {
                Console.WriteLine("次线程:" + callDuration);
                Thread.Sleep(1000);
            }

            threadId = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine(threadId.ToString());
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }

 private static int threadId;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //变量
            string rethrnValue = string.Empty;
            AsyncDemo ad = new AsyncDemo();

            //把AD对象的TestMethod方法注册给了AsyncMethodCaller委托,实例名CallbackEvent,
            //再由这个CallbackEvent去异步调用,BeginInvoke ,EndInvoke,Invoke等三个同异步方法
            //而为什么有this.invoke这个嘛!就不知跟这里所说的一样不,希望有朋友给我指出下,谢谢

            AsyncMethodCaller CallbackEvent = new AsyncMethodCaller(ad.TestMethod);


            //方法1,指定一个回调函数,可惜在回调函数里还是必须得用INVOKE,证明,
            //得到的数据还是在别的线程而已,而CONSOLE,MESSAGEBOX能用只能说是静态的
            //如果启动异步调用的线程不需要是处理结果的线程,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。
            //若要使用回调方法,必须将引用回调方法的 AsyncCallback 委托传递给 BeginInvoke。
            //也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。
            //主要理解回调方法将要使用的信息的对象,IAsyncResult,这个,也可以理解下
            //回调的方法  new AsyncCallback(Callback)
            //使用的委托  CallbackEvent
            //前两个是委托的参数

            CallbackEvent.BeginInvoke(3000, out threadId, new AsyncCallback(Callback), CallbackEvent);



            //方法2     一直轮循,一直都是UI线程在调,判断是否完成
            //MSDN:您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现异步调用何时完成。
            //从用户界面的服务线程中进行异步调用时可以执行此操作。
            //轮询完成允许调用线程在异步调用在 ThreadPool 线程上执行时继续执行。
            //尽管的确是界面也可以操作,但是,实际上来拖动界面是很难的,证明这时间是很短的,
            //对于显示一个固定的界面不能动的进度条还可以考虑下,其他的
            //愚见就算了吧,就用第一种吧,其实这下三种道理都是一样的,开启一线程在做,主线程等待,等其完成,取其返回值,
            //但我们知道,在NET1.1线程间想传值是很麻烦,而二点零回调等等,则帮我们做了这么些事情吧


            //IAsyncResult ar1 = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
            //while (!ar1.IsCompleted)
            //{
            //    //Thread.Sleep(20);
            //    //richTextBox1.Text += 8 + ""n";
            //}//循环直到异步完成
            //    rethrnValue = CallbackEvent.EndInvoke(out threadId, ar1);
            //richTextBox1.Text += rethrnValue + " " + threadId;

            //方法3  使用WAITONE,其实道理与轮循一样,或许只是少了个WHILE循环
            //MSDN :如果您使用 WaitHandle,则在异步调用完成之前或之后,在通过调用 EndInvoke 检索结果之前,还可以执行其他处理。
            //其实跟上下两种方法是一样的,感觉好拙呀!只是写法不同而已


            //IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
            ////  while (!result.IsCompleted)
            ////{
            ////    Application.DoEvents();
            ////}
            //result.AsyncWaitHandle.WaitOne();
            //rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
            //richTextBox1.Text += rethrnValue + " " + threadId.ToString();


            //方法四   一BeginInvoke,另一线程就启动,而在与EndInvoke之间,主线程是可以干这里面代码的事情的,
            //MSDN:EndInvoke 可能会阻止调用线程,因为它直到异步调用完成之后才返回。
            //这种技术非常适合文件或网络操作,但是由于 EndInvoke 会阻止它,所以不要从服务于用户界面的线程中调用它。

            //IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
            ////while (!result.IsCompleted)
            ////{
            ////    Application.DoEvents();
            ////}
            // rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
            //richTextBox1.Text += rethrnValue + " " + threadId.ToString();

        }

        private void Callback(IAsyncResult ar)
        {
            AsyncMethodCaller caller = (AsyncMethodCaller)ar.AsyncState;
            string s = caller.EndInvoke(out threadId, ar);
            //如果这样写,其实就是另开一个同步调用了,而把上面那个异步调用再做一遍,一时很傻的想法
            //放在这里是想说明下同步而已

            //string s = caller.Invoke(3000, out threadId);
          

            //而如果用下面这个,则会发生线程间调用的问题,提示不是从创建线程赋值的,一般这种问题都是没有与UI线程同步
           
//richTextBox1.Text += threadId;
            
            //与UI线程同步的写法
             this.Invoke(new InvokeMethodCaller(setText), s);
        }

        
        private string  setText(string s)
        {
            //到这里了,已经是跟UI是同一线程的了
            richTextBox1.Text += s;
            return s;
        }


而里面////的地方是我特意测试用的,偶尔可能还能让你体会一下,代码很短,或许你COPY一下就可以在自已机子上运行,但跟着抄一遍,最好是理解后自已写一份出来,或许收益更大吧

改天再把读取网络大的文件流,SOCKET等等结合下写出来,希望朋友指出错误,谢谢
加油EVERYONE

posted @ 2008-03-31 13:31  yellowyu  阅读(636)  评论(0编辑  收藏  举报