有时在程序中需要一些占用资源很大的处理,比如数据库更新操作,在一些比较简单的情况可以使用Application.DoEvent()方法,解决UI界面不友好的问题,但是如果是一些复杂的情况就没办法了,比如在处理中点击按钮以暂停当前处理。这时用第一个方法就不好了(因为按钮不会显示被按下)
这个时候就需要用异步委托或者启动另一个线程去处理复杂的聚集操作,但是如果在工作线程中需要更新用户的UI界面,并且在点击按钮后要停止那些工作线程,需要如何进行处理呢?
解决第一个问题的方法,就是在工作线程中调用Control.BeginInvoke方法(Control.Invoke用于同步调用,该调用阻塞当前线程直到UI返回结果)。
Control.BeginInvoke方法有一个重载方法
public IAsyncResult BeginInvoke(Delegate);
public virtual IAsyncResult BeginInvoke(Delegate, object[]);
第二个方法传递的是委托的参数
在工作线程中调用的方法中可以先判断是否在UI线程中,如果是就进行UI操作,如果不是在进行异步调用(control.begininvoke),在这里需要用到一个判断条件:Control.InvokeRequired,这个属性如果返回False,则代表当前就是UI线程,否则为工作线程
举个例子:
private void MakeLogs(string body)
{
if (InvokeRequired == false)
{
ListViewItem lvi = new ListViewItem(DateTime.Now.ToString());
lvi.SubItems.Add(body);
if (lvContent.Items.Count == 200)
{
lvContent.Items.RemoveAt(lvContent.Items.Count-1);
}
lvContent.Items.Insert(0,lvi);
}
else
{
MakeLogDelegate makeLogs = new MakeLogDelegate(MakeLogs);
BeginInvoke(makeLogs,new object[]{body});
}
}
解决第二个问题的方法是:在工作线程中周期性的检查按钮状态。比如界面有个“取消”按钮,当用户点击这个按钮后,将状态变量置位,然后工作线程就去检查这个状态变量,如果发现取消状态被置位,则停止当前线程
举个例子:
private void CheckState(out bool cancel)
{
if (InvokeRequired == false)
//在此需要判断线程是否为UI线程,因为如果在工作
//线程上,会导致两个线程(UI Thread、Work Thread)共同访问一个变量
{
cancel = (m_state==ImportState.canceled);
}
else
{
object outCancel = false; //此处的用意是:由于bool类型是值类型,
//在传入后进行装箱操作,导致其值不变化
CheckStateDelegate checkStateDelegate = new CheckStateDelegate(CheckState);
Invoke(checkStateDelegate,new object[]{outCancel});
//此处用Invoke而不用BeginInvoke的原因是:需要确实得到状态才
//能做返回,所以不能用异步调用
cancel = (bool)outCancel;
}
}