zoukankan      html  css  js  c++  java
  • .NET 实现异步处理的集中方式

    对于异步,相信大家都不十分陌生。准确点来说就是方法执行后立即返回,待到执行完毕会进行通知。就是当一个任务在执行的时候,尤其是需要耗费很长的时间进行处理的任务,如果利用单线程进行操作的话,势必造成界面的阻塞;而利用异步方式,则不会出现这种情况。 区别于同步处理,可以说阻塞的异步其实就相当于同步。

    同步方式的实现

    先来看一个同步的例子:

    假设现在我们需要导入文本文件的内容,然后对文件内容做处理。那么这就需要分为两步来进行,第一步是导入文本内容,我们利用函数A表示;第二部就是处理文本,我们利用函数B来表示。假设现在A不执行完,B不能进行。而且由于文本内容非常大,导入需要十几到几十分钟不等,那么我们得提示用户导入进度,这里就涉及到了界面交互问题。利用同步方式来做,效果如何呢?首先请看运行效果:

    #region 第一步:加载进入内存
            private void ReadIntoMemory()
            {
                if (String.IsNullOrEmpty(fileName))
                {
                    MessageBox.Show("文件名不能为空!");
                    return;
                }
    
                string result;
                long mainCount = 0;
                using (StreamReader sr = new StreamReader(fileName, Encoding.Default))
                {
                    while ((result = sr.ReadLine()) != null)
                    {
                        mainCount++;
    
                        recordList.Add(result); //添加记录到List中存储,以便在下一步进行处理。
    
                        double statusResult = (double)mainCount / (double)totalCount;
    
                        lblCurrentRecords.Text = mainCount.ToString();
                        lblStatus.Text = statusResult.ToString("p");
                        pbMain.Value = Int32.Parse((Math.Floor(statusResult)*100).ToString());
                    }
                }
            }
            #endregion
    
            #region 第二步:处理数据
            private void ProcessRecords()
            {
                if (recordList ==null)
                {
                    throw new Exception("数据不存在!");
                }
    
                if (recordList.Count==0)
                {
                    return;
                }
    
                int childCount = 0;
                int recordCount = recordList.Count;
    
                for (int i = 0; i < recordCount; i++)
                {
                    string thisRecord=recordList[i];
                    if (String.IsNullOrEmpty(thisRecord) || !thisRecord.Contains(","))
                    {
                        return;
                    }
    
                    string[] result = thisRecord.Split(',');
                   
                    ListViewItem lvi = new ListViewItem(result[0]);
    
                    for (int j = 1; j < result.Length; j++)
                    {
                        lvi.SubItems.Add(result[j]);
                    }
                    listItem.Add(lvi);
    
                    childCount++;
                    double percentage = (double)childCount / (double)recordCount;
                    pbChild.Value = Int32.Parse((Math.Floor(percentage) * 100).ToString());
                }
            }
            #endregion
    #region 开始进行处理
            private void btnLoad_Click(object sender, EventArgs e)
            {
                GetTotalRecordNum(); //得到总条数
    
                ReadIntoMemory();
                ProcessRecords();
            }
            #endregion

    我们是直接顺序运行的。之所以出现上面的情况,最主要就是界面处理和后台处理均糅合在了同一个线程之中,这样当后台进行数据处理的时候,会造成前台UI线程无法更新UI。要解决这种情况,当然是使用异步方式类处理:

    那么在.net编程中,有哪几种模式可以实现异步呢?

    4种异步方式

    1. ThreadPool.QueueUserworkItem实现
    2. APM模式(就是BeginXXX和EndXXX成对出现。)
    3. EAP模式(就是Event based, 准确说来就是任务在处理中或者处理完成,会抛出事件)
    4. Task

    上面总共4种方式中,其中在.net 2.0中常用的是(1),(2),(3),而在.net 4.0中支持的是(4),注意(4)在.net 2.0中是不能使用的,因为不存在。

    首先来说说ThreadPool.QueueUserWorkItem方式,也是最简单的一种方式。

    系统将需要运行的任务放到线程池中,那么线程池中的任务就有机会通过并行的方式进行运行。

    其次来说说APM模式

    这种模式非常常见,当然也是Jeff Richter极力推荐的一种方式。同时我也是这种模式的粉丝。这种模式的使用非常简单,就是利用Begin***的方式将需要进行异步处理的任务放入,然后通过End***的方式来接受方法的返回值。同时在Begin***和End***任务进行的过程中,如果涉及到界面UI的更新的时候,我们完全可以加入通知的功能。

    在Begin***和End***进行处理的时候,传递的是IAsyncResult对象,这种对象在Begin***中会承载一个委托对象,然后在End***中进行还原并得到返回值。

    如果你在设计的时候,需要有多个方法用到异步,并且想控制他们的运行顺序,请参考ManualResetEvent 和 AutoResetEvent方法,他们均是通过设置信号量来进行同步的。

    下面来看一个例子:

    假设现在我们需要导入文本文件的内容,然后对文件内容做处理。那么这就需要分为两步来进行,第一步是导入文本内容,我们利用函数A表示;第二部就是处理文本,我们利用函数B来表示。假设现在A不执行完,B不能进行。而且由于文本内容非常大,导入需要十几到几十分钟不等,那么我们得提示用户导入进度,这里就涉及到了界面交互问题。利用APM模式如何来做呢?首先请看运行效果:

    #region 典型的APM处理方式,利用Action作为无参无返回值的委托
            private void BeginReadIntoMemory()
            {
                Action action = new Action(ReadIntoMemory);
                action.BeginInvoke(new AsyncCallback(EndReadIntoMemory), action);
            }
    
            private void EndReadIntoMemory(IAsyncResult iar)
            {
                Action action = (Action)iar.AsyncState;
                action.EndInvoke(iar);
            }
    
            private void BeginProcessRecords()
            {
                Action action = new Action(ProcessRecords);
                action.BeginInvoke(new AsyncCallback(EndProcessRecords), action);
            }
    
            private void EndProcessRecords(IAsyncResult iar)
            {
                Action action = (Action)iar.AsyncState;
                action.EndInvoke(iar);
            }
            #endregion
    #region 开始进行处理,需要通过ManualResetEvent设置xinhaoilang的方式进行同步
            private void btnLoad_Click(object sender, EventArgs e)
            {
                GetTotalRecordNum(); //得到总条数
    
                BeginReadIntoMemory(); //读取数据到内存
                BeginProcessRecords(); //处理数据内容
            }
            #endregion

    在上面的代码段中,APM模式的处理方式很明显

    然后来说说EAP模式

    这种模式也很常见,准确来说就是在系统中通过申明委托事件,然后在执行过程中或者执行完毕后抛出事件。最常见的莫过于WebClient类的DownloadStringCompleted事件,这里我们将使用BackgroundWorker来进行讲解,虽然它本身就能够实现异步操作。在这里,我们只是用到了一个从文本中读取大数据量到内存的操作。图示如下:

    转自【http://www.cnblogs.com/scy251147/archive/2012/03/03/2378477.html

    屌丝一直很屌
  • 相关阅读:
    WPF窗体设计不符合微软自己的UX Guide
    NUnit测试WPF程序的一个小技巧
    Windows Vista中五花八门的菜单赏析
    忙碌文档
    怎样做才能算是一个UX良好的软件
    编写帮助文档经验总结
    [WPF Bug清单]之(7)——顽固的Error Template
    让NSIS生成的安装包在静默安装时从命令行窗口输出安装信息
    [WPF Bug清单]之(9)——消失的光标
    [提个醒] C#中yield return的小缺点
  • 原文地址:https://www.cnblogs.com/xiashenbin/p/3210335.html
Copyright © 2011-2022 走看看