下面是 C# 多线程与跨线程访问界面控件的方法完整攻略:
1. C# 多线程基础
在 C# 中,多线程的基本实现是通过 System.Threading 命名空间下的类来实现的。常用的类有:
- Thread:表示一个单独的线程。
- ThreadPool:表示一个线程池,它包含了多个预先创建的线程。
- Task:表示一个异步操作。
下面展示一个创建并运行线程的示例代码:
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Thread newThread = new Thread(Worker);
newThread.Start();
Console.ReadLine();
}
static void Worker()
{
Console.WriteLine("Thread started.");
Thread.Sleep(1000);
Console.WriteLine("Thread ended.");
}
}
上述代码会创建一个新线程并在该线程中运行 Worker 方法。Worker 方法简单地等待 1 秒钟后输出一段文本。在主线程中,我们等待用户按下回车键后才结束程序运行。
2. 跨线程访问控件
由于 C# 中 UI 控件是运行在主线程中的,因此我们不能直接在非主线程中访问它们。如果我们在非主线程中提供了一些 UI 操作的代码,尝试去更新 UI 控件的状态,将会抛出异常。因此,我们需要使用跨线程访问控件的方法来避免这个问题。
有多种方法可以实现跨线程访问控件,在这里我们介绍两种常见的方法。
2.1 Control.Invoke
Control.Invoke 方法可以在 UI 线程队列中执行指定的委托。下面是一个例子:
using System;
using System.Threading;
using System.Windows.Forms;
class Program
{
static Label label;
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form();
label = new Label();
label.Text = "Starting...";
label.Dock = DockStyle.Fill;
form.Controls.Add(label);
Thread newThread = new Thread(Worker);
newThread.Start();
Application.Run(form);
}
static void Worker()
{
DoUiWork("Thread started.");
Thread.Sleep(1000);
DoUiWork("Thread ended.");
}
delegate void StringArgReturningVoidDelegate(string text);
static void UpdateLabel(string text)
{
label.Text = text;
}
static void DoUiWork(string text)
{
if (label.InvokeRequired)
{
StringArgReturningVoidDelegate del = new StringArgReturningVoidDelegate(UpdateLabel);
label.Invoke(del, new object[] { text });
}
else
{
UpdateLabel(text);
}
}
}
在上述例子中,我们将一个标签加入了窗口中。在 Worker 方法中,我们通过 DoUiWork 方法来更新 label 对象的 Text 属性。该方法首先检查是否在 UI 线程中,如果在 UI 线程中,它会直接执行 UpdateLabel 方法,否则会通过 Control.Invoke 方法在 UI 线程中执行 UpdateLabel。使用 Control.Invoke 的时候需要注意:如果 UI 线程已经被阻塞,则调用 Control.Invoke 会导致死锁。
2.2 Control.BeginInvoke
Control.BeginInvoke 方法可以使一个委托异步地在 UI 线程中执行。与 Control.Invoke 不同,Control.BeginInvoke 不会阻塞当前线程。
using System;
using System.Threading;
using System.Windows.Forms;
class Program
{
static Label label;
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form();
label = new Label();
label.Text = "Starting...";
label.Dock = DockStyle.Fill;
form.Controls.Add(label);
Thread newThread = new Thread(Worker);
newThread.Start();
Application.Run(form);
}
static void Worker()
{
DoUiWork("Thread started.");
Thread.Sleep(1000);
DoUiWork("Thread ended.");
}
delegate void StringArgReturningVoidDelegate(string text);
static void UpdateLabel(string text)
{
label.Text = text;
}
static void DoUiWork(string text)
{
if (label.InvokeRequired)
{
StringArgReturningVoidDelegate del = new StringArgReturningVoidDelegate(UpdateLabel);
label.BeginInvoke(del, new object[] { text });
}
else
{
UpdateLabel(text);
}
}
}
上述代码与前一个例子很相似,唯一的不同在于我们换用了 Control.BeginInvoke 方法来切换到 UI 线程。具体用法也基本相同,我们通过 DoUiWork 方法来更新 label 对象的 Text 属性,方法中首先检查是否在 UI 线程中。如果在 UI 线程中,它会直接执行 UpdateLabel 方法,否则会通过 Control.BeginInvoke 方法在 UI 线程中异步执行 UpdateLabel。注意:使用 Control.BeginInvoke 的时候,我们不知道委托什么时候会被执行,因此需要小心使用。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#多线程与跨线程访问界面控件的方法 - Python技术站