WPF高度并发的跨线程UI访问

时间:2022-01-11 10:58:34

In C# I have several workers that do work and I need to reflect this into the UI. Update a progress bar, add items to a list and so on.

在C#中,我有几个工作,我需要将其反映到UI中。更新进度条,将项添加到列表等。

First time I tried to do this, I prepared everything with locks to make sure only one thread accesses the UI at every given moment but I got an exception. So I learned I need to use the Dispatcher.Invoke to access and change UI elements. It's quite cumbersome to enclose each UI change into an invoke but I can deal with it.

我第一次尝试这样做时,我用锁来准备一切,以确保每个给定时刻只有一个线程访问UI,但我得到了一个例外。所以我了解到我需要使用Dispatcher.Invoke来访问和更改UI元素。将每个UI更改包含在调用中是非常麻烦的,但我可以处理它。

My questions:

  • Is it necessary to synchronize the Dispatcher.Invoke calls or is that done internally? So I wonder if I need to add another layer of protection with locks...
  • 是否有必要同步Dispatcher.Invoke调用或内部完成?所以我想知道我是否需要用锁添加另一层保护......

  • Does it impact performance having many update requests queued into the dispatcher? So if several threads work and each of them reflects into the UI with a small change, how does issuing many calls to Dispatcher.Invoke` affect performance? Should I use a timer and only update UI once a second and queue all the UI changes internally?
  • 它是否会影响排队到调度员的许多更新请求的性能?因此,如果多个线程工作并且每个线程都通过一个小的更改反映到UI中,那么发出许多对Dispatcher.Invoke`的调用会如何影响性能?我应该使用计时器并且每秒只更新一次UI并在内部排队所有UI更改吗?

  • Is there an easier way to do cross-thread access more inline... without Invokes?
  • 是否有更简单的方法来进行跨线程访问更内联...没有调用?

2 个解决方案

#1


7  

The human brain won't be able to process 200 updates/sec. It will just slow down your UI and not gain you anything.

人脑将无法处理200次更新/秒。它只会减慢你的用户界面而不会获得任何收益。

Instead, make a timer that polls for status every, say, 200ms. This will be fast enough (5 updates/sec) not to be noticeable.

相反,制作一个计时器,每个轮询状态,比如200ms。这将足够快(5次更新/秒)不明显。

No need to lock on the dispatcher, it's handled internally. But you should dedicate your tasks to the computations, and not manipulate the UI from them. Implement a timer like I said, and use some standard way of cross-thread communication to retrieve the current status from the UI thread.

无需锁定调度程序,它是在内部处理的。但是你应该将你的任务专门用于计算,而不是从他们那里操纵UI。像我说的那样实现一个计时器,并使用一些标准的跨线程通信方式从UI线程中检索当前状态。

#2


3  

Is it necessary to synchronize the Dispatcher.Invoke calls or is that done internally? So I wonder if I need to add another layer of protection with locks...

是否有必要同步Dispatcher.Invoke调用或内部完成?所以我想知道我是否需要用锁添加另一层保护......

No, it is redundant.

不,这是多余的。

Does it impact performance having many update requests queued into the dispatcher? So if several threads work and each of them reflects into the UI with a small change, how does issuing many calls to Dispatcher.Invoke` affect performance? Should I use a timer and only update UI once a second and queue all the UI changes internally?

它是否会影响排队到调度员的许多更新请求的性能?因此,如果多个线程工作并且每个线程都通过一个小的更改反映到UI中,那么发出许多对Dispatcher.Invoke`的调用会如何影响性能?我应该使用计时器并且每秒只更新一次UI并在内部排队所有UI更改吗?

If you use Invoke and not BeginInvoke, you won't fill the queue (unless you have too many threads), each thread will just wait for its action to be processed by the dispatcher.

如果您使用Invoke而不是BeginInvoke,则不会填充队列(除非您有太多线程),每个线程将只等待调度程序处理其操作。

Spamming dispatcher may affect performance. You don't need to update UI too often. If you constantly wait for dispatcher, you'll just waste time and lose the benefits of multithreading.

垃圾邮件调度程序可能会影响性能。您不需要经常更新UI。如果你经常等待调度员,你只会浪费时间并失去多线程的好处。

A better option is to call BeginInvoke and do it not too often to avoid overwhelming main thread. This way, you won't waste time of the processing thread while waiting for UI to update.

一个更好的选择是调用BeginInvoke并且不要经常这样做以避免压倒主线程。这样,在等待UI更新时,您不会浪费处理线程的时间。

Is there an easier way to do cross-thread access more inline... without Invokes?

是否有更简单的方法来进行跨线程访问更内联...没有调用?

  1. If you can put worker code inline, then async/await:

    如果你可以将工作代码放入内联,那么async / await:

    UpdateUI();
    await Task.Run(...);
    UpdateUI();
    
  2. If you use Paraller.ForEach, you can put progress value into a varaiable, atomically increment it, and update UI using timer.

    如果您使用Paraller.ForEach,您可以将进度值放入可变的,原子地递增它,并使用计时器更新UI。

#1


7  

The human brain won't be able to process 200 updates/sec. It will just slow down your UI and not gain you anything.

人脑将无法处理200次更新/秒。它只会减慢你的用户界面而不会获得任何收益。

Instead, make a timer that polls for status every, say, 200ms. This will be fast enough (5 updates/sec) not to be noticeable.

相反,制作一个计时器,每个轮询状态,比如200ms。这将足够快(5次更新/秒)不明显。

No need to lock on the dispatcher, it's handled internally. But you should dedicate your tasks to the computations, and not manipulate the UI from them. Implement a timer like I said, and use some standard way of cross-thread communication to retrieve the current status from the UI thread.

无需锁定调度程序,它是在内部处理的。但是你应该将你的任务专门用于计算,而不是从他们那里操纵UI。像我说的那样实现一个计时器,并使用一些标准的跨线程通信方式从UI线程中检索当前状态。

#2


3  

Is it necessary to synchronize the Dispatcher.Invoke calls or is that done internally? So I wonder if I need to add another layer of protection with locks...

是否有必要同步Dispatcher.Invoke调用或内部完成?所以我想知道我是否需要用锁添加另一层保护......

No, it is redundant.

不,这是多余的。

Does it impact performance having many update requests queued into the dispatcher? So if several threads work and each of them reflects into the UI with a small change, how does issuing many calls to Dispatcher.Invoke` affect performance? Should I use a timer and only update UI once a second and queue all the UI changes internally?

它是否会影响排队到调度员的许多更新请求的性能?因此,如果多个线程工作并且每个线程都通过一个小的更改反映到UI中,那么发出许多对Dispatcher.Invoke`的调用会如何影响性能?我应该使用计时器并且每秒只更新一次UI并在内部排队所有UI更改吗?

If you use Invoke and not BeginInvoke, you won't fill the queue (unless you have too many threads), each thread will just wait for its action to be processed by the dispatcher.

如果您使用Invoke而不是BeginInvoke,则不会填充队列(除非您有太多线程),每个线程将只等待调度程序处理其操作。

Spamming dispatcher may affect performance. You don't need to update UI too often. If you constantly wait for dispatcher, you'll just waste time and lose the benefits of multithreading.

垃圾邮件调度程序可能会影响性能。您不需要经常更新UI。如果你经常等待调度员,你只会浪费时间并失去多线程的好处。

A better option is to call BeginInvoke and do it not too often to avoid overwhelming main thread. This way, you won't waste time of the processing thread while waiting for UI to update.

一个更好的选择是调用BeginInvoke并且不要经常这样做以避免压倒主线程。这样,在等待UI更新时,您不会浪费处理线程的时间。

Is there an easier way to do cross-thread access more inline... without Invokes?

是否有更简单的方法来进行跨线程访问更内联...没有调用?

  1. If you can put worker code inline, then async/await:

    如果你可以将工作代码放入内联,那么async / await:

    UpdateUI();
    await Task.Run(...);
    UpdateUI();
    
  2. If you use Paraller.ForEach, you can put progress value into a varaiable, atomically increment it, and update UI using timer.

    如果您使用Paraller.ForEach,您可以将进度值放入可变的,原子地递增它,并使用计时器更新UI。