编写线程安全的UI程序

本篇讲一下如何编写线程安全的Winform 程序

有问题的代码

一个很简单的功能,Winform上实时显示当前时间:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public partial class Form1 : Form
{
    private System.Threading.Timer _timer;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        _timer = new System.Threading.Timer(UpdateTime, null, 0, 20);
    }

    private void UpdateTime(object state)
    {
        lblTime.Text = DateTime.Now.ToString("hh:mm:ss");
    }
}

F5 运行,出错了: smbv1 这是因为System.Threading.Timer并不是在lblTimer所在的UI线程上运行,而是在线程池上运行。

使用Invoke/BeginInvoke

解放方法也很简答,用 Invoke() 或者 BeginInvoke()

  • control.Invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。

  • control.BeginInvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public partial class Form1 : Form
{
    private System.Threading.Timer _timer;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        _timer = new System.Threading.Timer(UpdateTime, null, 0, 20);
    }

    private void UpdateTime(object state)
    {
        try
        {
            Invoke(new MethodInvoker(() =>
            {
                lblTime.Text = DateTime.Now.ToString("hh:mm:ss");

            }));
        }
        catch (Exception e大专栏  编写线程安全的UI程序s="p">) when (e is ObjectDisposedException)
        {
        }
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _timer.Change(Timeout.Infinite, Timeout.Infinite);
    }
}

使用 SynchronizationContext.Send 或者 Post方法

使用 SynchronizationContext 同步上下文, 它表示一个代码要执行的地方,不一定是一个线程,可能是多个线程,可能是一个CPU核心,也可能是一个网络上的主机。 他有两个主要方法:

  • SynchronizationContext.Send(SendOrPostCallback d,object state);

  • SynchronizationContext.Post(SendOrPostCallback d,object state);

Send() :在当前线程上去调用委托来实现(同步调用)。也就是在子线程上直接调用UI线程执行,等UI线程执行完成后子线程才继续执行。调用 Invoke()实现

Post() 是在线程池上去调用委托来实现(异步调用)。这是子线程会从线程池中找一个线程去调UI线程,子线程不等待UI线程的完成而直接执行自己下面的代码。调用 BeginInvoke()实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public partial class Form1 : Form
{
    private System.Threading.Timer _timer;
    private readonly SynchronizationContext _context;

    public Form1()
    {
        InitializeComponent();
        _context = SynchronizationContext.Current ?? new SynchronizationContext();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        _timer = new System.Threading.Timer(UpdateTime, null, 0, 20);
    }

    private void UpdateTime(object state)
    {
        //_context.Post(delegate { lblTime.Text = DateTime.Now.ToString("hh:mm:ss");}, null);
        _context.Send(delegate { lblTime.Text = DateTime.Now.ToString("hh:mm:ss"); }, null);
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _timer.Change(Timeout.Infinite, Timeout.Infinite);
    }
}

参考链接

猜你喜欢

转载自www.cnblogs.com/liuzhongrong/p/12408047.html