• 客户端多线程应用的好工具:BackgroundWorker


    WINCE项目上线,可以喘口气,可以来优化一下后台系统关键的操作。

    B2C后台系统订单流程的几个关键点,下单,开始拣货,发货,这次就从开始拣货改起。

    开始拣货由于涉及表众多,而且涉及到多个仓库的高并发,所以其中数据控制尤为重要。在之前的方法中,我们加了很多重限制,比如Serializable隔离级别的事务,对Nhibernate读取出来的关键对象加写锁防止脏读。最后改到批量处理时客户端服务器一次提交一单防止跨多个Service产生的脏数据。这样改来数据的安全性有了保障,但是性能是有些问题的。不过这个功能性能需求远远要下雨数据完整性的需求。

    之前的客户端采用的是单线程方式,并且操作方式不甚友好,选中需要打印的订单开始拣货后,整个页面是无法响应的,也就是这个客户端程序基本上是不能做其它动作的,并且拣货结果也要等一次提交完成之后才能反馈,如果能改成自动的,自动查询自动提交,这样肯定会节省很多人力。

    所以综合以上需求,我们需要完成如下功能,

    1.一个自动实现查询,提交这样功能的客户端程序。

    2.必须要异步,这样可以在这个操作进行的时候程序可以进行其他的功能。

    3.必须要中途可以实时汇报进度,输出反馈。

    4.这个提交可能涉及到的数据条数百条级别,所以要随时可以中途停止。

    这样的需求,微软提供给我们的BackgroundWorker既可以完美实现

    MSDN地址:  http://msdn.microsoft.com/zh-cn/library/system.componentmodel.backgroundworker%28v=vs.80%29.aspx

    UI设计如下

    可以看见,上面是订单过滤的查询条,左侧是将要操作的数据,右侧是操作的输出结果,下面是操作方式以及操作进度条

    好吧,界面搞定了,我们开始编码

    首先生命一个BackgroundWorker,

    private System.ComponentModel.BackgroundWorker backgroundWorkerPick;

    因为要在运行时报告进度,还要随时可以停止,所以修改属性

                this.backgroundWorkerPick.WorkerReportsProgress = true;
    this.backgroundWorkerPick.WorkerSupportsCancellation = true;

    然后加上加上Do_work,complete,processing的委托

            this.backgroundWorkerPick.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorkerPick_DoWork);
    this.backgroundWorkerPick.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorkerPick_ProgressChanged);
    this.backgroundWorkerPick.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorkerPick_RunWorkerCompleted);
    //


    BackgroundWorker属性设置完成,下面实现3个事件

       private void backgroundWorkerPick_DoWork(object sender, DoWorkEventArgs e)
    {
    CleanListBox();
    if (_controller._pendingOrders.Count == 0)
    {
    MessageBox.Show("无可用订单");
    return;
    }
    var OrderCount = _controller._pendingOrders.Count;

    foreach (var order in _controller._pendingOrders)
    {
    if (backgroundWorkerPick.CancellationPending)
    break;

              //这里做事务提交,调用service操作
    var result = _controller.PickOrder(order, _controller.WareshouseId);
             _controller._handledOrders.Add(order);

    if (result.Result == 0)
    _controller._successOrders.Add(order);
    else
    {
    _controller._failedOrders.Add(order);
    }
    int percent =(int)((_controller._handledOrders.Count / (float)OrderCount) * 100);

             //将结果传入Progress事件
    backgroundWorkerPick.ReportProgress(percent, result);

    }


    }



    private void backgroundWorkerPick_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    DoSearch(false, false);
           //如果是勾选了轮询操作,则做下一次Do_work

    if (isLootPick)
    backgroundWorkerPick.RunWorkerAsync();
    else
    EnableControls(true);
    }

    private void backgroundWorkerPick_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    var i = e.ProgressPercentage;

           //调用委托去更新状态条
    ShowProgress(i);
    lbPickResult.Text = string.Format("拣货结果:共操作{0}个,成功{1}个,失败{2}个",
    _controller._handledOrders.Count, _controller._successOrders.Count,
    _controller._failedOrders.Count);
    SvcResult result = (SvcResult)e.UserState;
            //输出结果到LISTBOX
    if (result.Result != 0)
    lvResult.Items.Add(result.ErrorMessage.Aggregate(string.Concat));

    Application.DoEvents();
    }

    开始自动拣货跟结束自动拣货BUTTON的事件

    private void buttonCreatePicks_Click(object sender, EventArgs e)
    {

    if (!backgroundWorkerPick.IsBusy)
    {
    DoSearch(false, true);
    EnableControls(false);
    prog.Value = 0;
    //lbPickResult.Text = "";

    backgroundWorkerPick.RunWorkerAsync();
    }
    }

    private void btnEnd_Click(object sender, EventArgs e)
    {
    if (backgroundWorkerPick.IsBusy)
    {
    backgroundWorkerPick.CancelAsync();
    EnableControls(true);
    checkBox1.Checked = false;
    isLootPick = false;
    }
    }

    设置ProgressBar,跟实时输入信息的Lable信息,还有清空ListBox控件信息。

    由于是子线程中涉及到父窗体控件的设置,所以使用委托来处理

            private delegate void ShowProgressCallBack(int percent);
    private void ShowProgress(int percent)
    {
    if (prog.InvokeRequired)
    {
    var callback = new ShowProgressCallBack(ShowProgress);
    Invoke(callback, new object[] { percent });
    return;
    }

    prog.Value = percent;
    }

    private delegate void CleanListBoxCallBack();
    private void CleanListBox()
    {
    if (lvResult.InvokeRequired)
    {
    var callback = new CleanListBoxCallBack(CleanListBox);
    Invoke(callback);
    return;
    }

    lvResult.Items.Clear();
    }

    自动拣货CHECKBOX属性设置,可以注意到我在backgrondWorker_Complete事件里面使用到了这个属性,来控制是否进行下一轮操作

            private bool isLootPick = false;

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
    isLootPick = checkBox1.Checked;
    }

    这个listBox_Keypress事件是用来复制Listbox中信息的方法

            private void listBoxFileList_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
    {
    if (e.KeyChar == 3) //Crtl+C
    {
    //Clipboard.SetDataObject(lvResult.SelectedItem.ToString());
    //Clipboard.SetDataObject(lvResult.SelectedItems.Trim());
    string val = "";
    for (int i = 0; i < lvResult.SelectedItems.Count; i++)
    {
    val += lvResult.SelectedItems[i].ToString() + "\r\n";
    }
    Clipboard.SetDataObject(val);
    }
    }

    最后一个方法,是查询要处理的数据集方法,其中涉及到了一些控件状态的设置,利用default参数控制

     private void DoSearch(bool justForQuery = false, bool needRefeshLable = false)
            {
                EnableControls(false);
    
                //非循环开始拣货时刷新LABLE
                if (needRefeshLable)
                {
                    lbPickResult.Text = "查询订单中";
                    Application.DoEvents();
                }
    
                _controller._dto.OrderType = (int)cbOrderType.SelectedValue;
                _controller._dto.OrderStartDate = dtpOrderDate.Value;
                _controller._dto.OrderEndDate = dtpOrderEndDate.Value;
    
                if (cbWareHouse.SelectedItem != null)
                {
                    _controller._dto.WareHouseId = (long)cbWareHouse.SelectedValue;
                }
    
                _controller.DoSearchEvent();
                dgvResult.DataSource = null;
                lbSearchResult.Text = string.Format("查询结果:所有可操作订单数{0}个", _controller._pendingOrders.Count);
                dgvResult.DataSource = _controller._pendingOrders;
                _controller.WareshouseId = (long)cbWareHouse.SelectedValue;
                SetDataGridViewStyle(dgvResult);
    
                //单查询刷新LABLE,以及控件状态
                if (justForQuery)
                {
                    EnableControls(true);
                }
                //非循环开始拣货时刷新LABLE
                if (needRefeshLable)
                {
                    lbPickResult.Text = "查询完成";
                    Application.DoEvents();
                }
    
    
            }
    

    OK,工作基本完成,需求基本全部实现。功能就是利用backgroundworker实现了客户端的异步多线程操作,其中的方法细节基本可以涵盖此控件的一般使用。








  • 相关阅读:
    0100相同的树 Marathon
    0017电话号码的字母组合 Marathon
    0236二叉树的最近公共祖先 Marathon
    1365有多少小于当前数字的数字 Marathon
    0701二叉搜索树的插入操作 Marathon
    0538二叉搜索树转为累加树 Marathon
    0039组合总和 Marathon
    0098验证二叉搜索树 Marathon
    0700二叉搜索树中的搜索 Marathon
    0108有序数组转为二叉搜索树 Marathon
  • 原文地址:https://www.cnblogs.com/vinnie520/p/2434534.html
Copyright © 2020-2023  润新知