面试题答案
一键面试可能遇到的问题
- 线程安全问题:在多线程环境下,委托的调用列表可能会在调用过程中被其他线程修改,导致调用异常。例如,当一个线程正在遍历委托的调用列表并执行方法时,另一个线程移除了列表中的某个方法,就会引发
InvalidOperationException
。 - 跨线程访问UI控件问题:如果在非UI线程中引发与UI相关的事件,直接更新UI控件会导致跨线程操作异常,因为在Windows Forms和WPF中,只有创建UI控件的线程才能访问和更新该控件。
解决方法及代码示例
1. 解决委托调用列表修改的线程安全问题
using System;
using System.Threading;
public class ThreadSafeEventExample
{
private EventHandler _eventHandler;
// 线程安全的添加事件处理方法
public void AddHandler(EventHandler handler)
{
lock (this)
{
_eventHandler += handler;
}
}
// 线程安全的移除事件处理方法
public void RemoveHandler(EventHandler handler)
{
lock (this)
{
_eventHandler -= handler;
}
}
// 引发事件,线程安全
public void RaiseEvent()
{
EventHandler localCopy;
lock (this)
{
localCopy = _eventHandler;
}
localCopy?.Invoke(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
var example = new ThreadSafeEventExample();
example.AddHandler((sender, e) => Console.WriteLine("Event handled in thread {0}", Thread.CurrentThread.ManagedThreadId));
Thread thread1 = new Thread(() => example.RaiseEvent());
Thread thread2 = new Thread(() => example.RaiseEvent());
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
在上述代码中,通过 lock
关键字来确保在添加、移除事件处理方法以及引发事件时,委托调用列表的一致性,避免了多线程环境下对调用列表的并发修改问题。
2. 解决跨线程访问UI控件问题(以Windows Forms为例)
using System;
using System.Windows.Forms;
using System.Threading;
public partial class Form1 : Form
{
private event EventHandler _uiUpdateEvent;
public Form1()
{
InitializeComponent();
_uiUpdateEvent += UpdateUI;
}
private void UpdateUI(object sender, EventArgs e)
{
label1.Text = "UI updated in thread " + Thread.CurrentThread.ManagedThreadId;
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
// 跨线程引发事件
if (InvokeRequired)
{
Invoke(_uiUpdateEvent, this, EventArgs.Empty);
}
else
{
_uiUpdateEvent(this, EventArgs.Empty);
}
});
thread.Start();
}
}
在这个Windows Forms示例中,当按钮被点击时,启动一个新线程。在新线程中引发 _uiUpdateEvent
事件,通过 InvokeRequired
和 Invoke
方法来确保在UI线程上执行更新UI的操作,从而避免跨线程操作异常。如果是在WPF中,可以使用 Dispatcher
来达到类似的效果。