多线程,一个古老的话题,今天我来聊下最基本的线程,UI线程和后台线程。
在后台线程中直接操作UI控件会出现异常(线程间操作无效:从不是创建控件“XX”的线程访问它)。怎么解决这个问题那?关键点在代理和InvokeRequired属性,Winform的UI(窗口)代码如下:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using LandpyLibrary;
using LandpyCommon;
namespace LandpyWindowsForm
{
public partial class MainForm : Form, IFormWorkThread
{
public MainForm()
{
InitializeComponent();
cdObj = SetTime;
}
private delegate void ControlDelegate(string time);
private WorkThread wtObj;
ControlDelegate cdObj;
private void SetTime(string time)
{
lblTime.Text = time;
}
public void ShowTime(string time)
{
if (lblTime.InvokeRequired)
{
Invoke(cdObj, time);
}
else
{
SetTime(time);
}
}
private void btnStart_Click(object sender, EventArgs e)
{
wtObj = new WorkThread(this);
wtObj.StartShowTime();
}
private void btnEnd_Click(object sender, EventArgs e)
{
if (wtObj != null)
{
wtObj.EndShowTime();
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (wtObj != null)
{
wtObj.EndShowTime();
}
}
}
}
为了能在后台线程中操作UI线程,我们的MainForm继承了公共接口IFormWorkThread,接口定义如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace LandpyCommon
{
public interface IFormWorkThread
{
void ShowTime(string time);
}
}
该接口放在一个独立的Dll中,UI线程和后台线程也均处于不同的程序集中,它们均引用了该接口的Dll。
后台线程类如下:
using System.Collections.Generic;
using System.Text;
using System.Threading;
using LandpyCommon;
namespace LandpyLibrary
{
public class WorkThread
{
private IFormWorkThread _formWorkThread;
private Thread thread;
public WorkThread(IFormWorkThread formWorkThread)
{
_formWorkThread = formWorkThread;
}
public void Deal()
{
while (true)
{
Thread.Sleep(1000);
_formWorkThread.ShowTime(DateTime.Now.ToString());
}
}
public void StartShowTime()
{
thread = new Thread(new ThreadStart(Deal));
thread.Name = "TimeThread";
thread.Start();
}
public void EndShowTime()
{
thread.Abort();
}
}
}
其实报异常是因为多线程操作UI控件是非安全的,MS提供了解决方案,关键代码如下:
public MainForm()
{
InitializeComponent();
cdObj = SetTime;
}
private delegate void ControlDelegate(string time);
ControlDelegate cdObj;
private void SetTime(string time)
{
lblTime.Text = time;
}
public void ShowTime(string time)
{
if (lblTime.InvokeRequired)
{
Invoke(cdObj, time);
}
else
{
SetTime(time);
}
}
通过代理和InvokeRequired属性解决了这个问题。