客户端多线程应用的好工具: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实现了客户端的异步多线程操作,其中的方法细节基本可以涵盖此控件的一般使用。








原文地址:https://www.cnblogs.com/vinnie520/p/2434534.html