• C#桌面应用使用异步计算改善界面效果的两种方式


    在桌面应用中,我们希望当一个费时的运算在进行的时候,当前窗体可以有所表现,比如显示等待动画或者进度条,避免让用户陷入无聊、乏味又不知何时才能执行完成的苦闷之中,此时异步计算即可派上用场,所谓异步,即是将费时的运算放到一个专门的工作线程里面去,不在当前UI线程里面处理,如果在UI线程内处理,UI势必会进入假死状态,期间用户无法移动窗体,无法取消费时操作,只能等待程序处理完毕,程序的控制权才会重新交换给用户,这种方式前面也说了,相当的不友好!其实这里的异步操作类似于WEB开发中常用的AJAX,调用XMLHTTP在后台操作,等操作完成后调用回调函数,给用户结果,控制权一直都在用户手里,感觉肯定不一样。:)

    好了,说了使用和不用异步运算的区别,我们下边说说在.NET中,使用C#语言,实现异步操作的两种方式:

    一,使用delegate代理

    1、假如需要在工作线程进行长时间工作的方法是:int StartExportData()

    //导出旧库
    public int StartExportData()
    {
        
    return new ExportData().StartExportDataToTmp();
    }

    2、定义上述方法的委托方法

    //StartExportData方法委托,异步调用StartExportData方法,避免主线程阻塞。
    delegate int StartExportDataDelegate();

    3、定义回调方法,指示当StartExportData()执行完成后所进行的操作

    //回调方法,当StartExportData方法执行完毕返回后执行的回调。
    public void ExportCallBackMethod(System.IAsyncResult exportResult)
    {
        
    //获得委托对象
        StartExportDataDelegate d1 = (StartExportDataDelegate)exportResult.AsyncState;
        
    //EndInvoke方法必须调用,取得被委托方法的返回值
        int result = d1.EndInvoke(exportResult);
        
    if (result == 1)    //如果返回为1,表示外部程序成功返回
        {
            ShowMessage(
    "成功导出旧年度数据库 ...");    //更新信息提示

        }
        d1 
    = null;
    }

    4、在新的工作线程内启动被代理的方法

    //初始化委托方法对象
    StartExportDataDelegate d1 = new StartExportDataDelegate(StartExportData);
    //初始化回调方法对象
    System.AsyncCallback c1 = new System.AsyncCallback(ExportCallBackMethod);
    //使用BeginInvoke方法启动异步线程,在线程内执行被委托方法StartExportData,
    //线程执行完毕后调用回调方法CallBackMethod
    d1.BeginInvoke(c1, d1);

    5、最后,要说明的是,需要引入Threading:

    using System.Threading;

    二、使用BackgroundWorker 类,它是在 .NET Framework 2.0 版中是新增的。

    还是对上述方法的异步调用,换用BackgroundWorker后会是怎样的呢?我们一起来看一下:

    1、初始化类的两个事件:DoWork和RunWorkerCompleted,顾名思义,DoWork即为工作线程,在该方法内调用工作方法; RunWorkerCompleted即为回调方法,即为工作方法完成后所调用的方法。这里的bgWorker为BackgroundWorker类的实例。

    /// <summary>
    /// 初始化工作线程事件处理程序
    /// </summary>
    private void initBackgroundWorker()
    {
        bgWorker.DoWork 
    += new DoWorkEventHandler(bgWorker_DoWork);
        bgWorker.RunWorkerCompleted 
    += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
    }

    2、在类的构造函数内调用上述初始化方法。

    public BaseDataForm()
    {
        InitializeComponent();
        initBackgroundWorker();
    }

    3、修改方法的签名,增加两个参数。

    private int StartExportData(BackgroundWorker worker, DoWorkEventArgs e)
    {
        
    try
        {
            ... ...
        }
        
    finally
        {

        }
        
    return 0;
    }

    4、实现DoWork方法。

    /// <summary>
    /// 工作线程所做工作
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker 
    = sender as BackgroundWorker;
        e.Result 
    = process(worker, e);
        
    //以下为接受多个参数的方法,一个参数就不用说了吧 :)
       
    //string arg1 = ((ArrayList)e.Argument)[0].ToString();
        
    //string arg2 = ((ArrayList)e.Argument)[1].ToString();
        
    //e.Result = compare(arg1, arg2 , worker, e);
    }

    5、实现RunWorkerCompleted方法。

    /// <summary>
    /// 工作线程结束后所做工作
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        
    if (e.Error != null)
        {
            ShowMessage(ResUtils.getString((
    "TaskError")));
            btnStart.Enabled 
    = true;
            btnClose.Enabled 
    = true;
            picBox.Enabled 
    = false;
            MessageBox.Show(e.Error.Message, SystemContext.APP_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error);
            
    if (log.IsErrorEnabled)
            {
                log.Error(e.Error.Message, e.Error);
            }
        }
        
    else
        {
            ShowMessage(ResUtils.getString((
    "TaskSuccess")));
            btnStart.Enabled 
    = true;
            btnClose.Enabled 
    = true;
            picBox.Enabled 
    = false;
        }
    }

    6、启动后台工作线程。

    private void btnStart_Click(object sender, EventArgs e)
    {
        
    if (check())
        {
            btnStart.Enabled 
    = false;
            btnClose.Enabled 
    = false;
            bgWorker.RunWorkerAsync();
    //这里可以添加一个参数对象,如果单个参数直接传,如果多个参数封装到类或ArrayList

        
    //以下为传送多个参数的方法,一个参数就不用说了吧 :)
        
    //ArrayList arg = new ArrayList(2);
        
    //arg.Add(txtExcelFile.Text);
        
    //arg.Add(cmbReportType.Text);
        
    //bgWorker.RunWorkerAsync(arg); //晕啊,传多个参数竟然非要用数组或者对象
        }
    }

    7、最后,同样,要引入的类为:

    using System.Threading;

    事实上,没有什么要比发现、培养、呵护、调整自己的心智的力量更重要的事情了.........

  • 相关阅读:
    [数据结构]线性表
    对C语言中指针的一些新认识
    Qt做动画旋转旋转图片
    延时程序执行Qt
    关于部分网页打不可的问题
    关于QString中的arg()函数使用方法
    Qt5.3.0 for Android开发环境配置
    QMenu,contextmenuevent,窗体透明
    Qt自定义窗体,边框,圆角窗体
    一个良好的团队
  • 原文地址:https://www.cnblogs.com/appleseed/p/1331830.html
Copyright © 2020-2023  润新知