本文帮助开发人员克服他们在使用后台线程与用户界面进行交互时Microsoft .NET Compact Framework的局限性,具体内容包括:多线程和用户界面基础知识和构建更好的类。
亡羊补牢
为了使我们的应用程序重新稳定,我们需要修改代码,这样所有与列表框的交互都会在主应用程序线程上发生。通过使用列表框上的 Invoke 方法,我们可以修改代码。Invoke 方法由 System.Windows.Forms.Control 基类提供,因此由所有的 Windows 窗体控件公开。Control.Invoke 方法在最初创建控件的线程上运行某个委托,允许该委托安全地与控件交互。
注.NET Framework 实现可以运行任何委托,与此不同,Control.Invoke 的 .NET Compact Framework 实现只支持 EventHandler 委托。
class MyForm : Form{
ListBox lbData ;
MyForm() {
InitializeComponent(); // Create form controls
Thread t = new Thread(new ThreadStart(Work1_));
t.Start() ; // Runs Work1_ on a background thread
}
private Queue qData = new Queue(); // Visible to all member functions on all threads
void Work1_(){
// Wrap AddItem in delegate
EventHandler eh = new EventHandler(AddItem);
StreamReader rdr1 = new StreamReader(@"\My Documents\DataFile.dat");
string line = rdr1.ReadLine();
while(line != null) {
lock(qData){ // Synchronize queue acess
qData.Enqueue(line); // Store line value in queue
}
lbData.Invoke(eh); // Transfer control to thread that created lbData
line = rdr1.ReadLine();
}
}
void AddItem(object o, EventArgs e)
{
string line = null;
lock(qData){ // Synchronize queue acess
line = (string)qData(); // Get data from queue
}
lbData.Items.Add(line); // Update list box
}
}
|
应用程序又稳定了。通过将修改列表框内容的代码移动到 AddItem 函数中,并将它包装到一个 EventHandler 委托中,我们已经将后台任务从它与 UI 的交互中分离出来。循环的每次传递期间,Work1_ 将从文件读取的数据放置到 qData 队列中并调用 lbData.Invoke 来运行包装 AddItem 函数的 EventHandler 委托。每次调用 lbData.Invoke 会挂起运行后台线程,直到主应用程序线程完成运行 AddItem 方法。AddItem 运行在主应用程序线程上,它从队列中提取值并将其安全地添加到列表框中。
克服局限性
对于简单的线程方案,Control.Invoke 的 .NET Compact Framework 实现很适用,但与 .NET Framework 实现相比却具有明显的局限性。
传递参数
首先,.NET Framework 提供了 Control.Invoke 的一种重载,它接受一个对象数组。用该对象数组将参数传递给执行的委托。
通过使用 .NET Framework 中的 Control.Invoke 重载,我们不再需要使用队列或者任何其他的数据结构在线程之间共享数据。数据可以只是作为委托调用的一部分而传递,明显地简化了在后台与 UI 线程之间的数据传递。
使用Control.Invoke重载生成下面Work1_ 与 AddItem的实现。
void Work1_(){
// Wrap AddItem in delegate
EventHandler eh = new EventHandler(AddItem);
StreamReader rdr1 = new StreamReader(@"\My Documents\DataFile.dat");
string line = rdr1.ReadLine();
while(line != null) {
lbData.Invoke(eh, new object[]{line, EventArgs.Empty}); // Pass to AddItem
line = rdr1.ReadLine();
}
}
// o receives the reference to line, e receives EventArgs.Empty
void AddItem(object o, EventArgs e)
{
string line = (string) o; // Upcast o
lbData.Items.Add(line); // Add to list box
}
|