我有一个多线程应用程序,其中包含一些后台处理。它具有长时间运行的UI更新(在UI线程本身上),这些更新通过众所周知的MSDN资源从后台线程调用。由于最终在外部库(1)中完成这些UI更新,因此我无法缩短它们。
现在,我想要在后台线程中异步地使用
我的当前解决方案是:在执行UI线程上的方法中,我会进入并退出ReaderWriterLockSlim实例。在后台线程中,我尝试使用零超时进入该实例。当成功时,我调用'BeginInvoke()'然后再次退出。当不成功时,我完全跳过方法调用。
这个方法可以工作,但是否有更优雅的解决方案呢?如何仅从一个线程简单地测试某个方法是否在任何(其他)线程上执行?
注意:使用Invoke()(而不是BeginInvoke())不是选项,因为这会阻止我的后台线程,从而防止其他东西在那里执行。
(1)这是一个地图解决方案MapXtreme,我想平移/缩放大型位图地形数据,并更新一些特性。
PS。这个问题有点相关,但涵盖了不同的方面:Winforms multithreading: Is creating a new delegate each time when invoking a method on the UI thread needed? 感谢任何答案!
更新:Hans Passant帮助了我,看到下面的解决方案。希望这也能帮助别人。
现在,我想要在后台线程中异步地使用
BeginInvoke()
调用这些更新到UI线程上,但仅在先前的更新已经完成时才进行。如果没有完成,我希望简单地跳过此更新。这将防止Windows消息队列溢出,以防调用比被调用方法更快。我的当前解决方案是:在执行UI线程上的方法中,我会进入并退出ReaderWriterLockSlim实例。在后台线程中,我尝试使用零超时进入该实例。当成功时,我调用'BeginInvoke()'然后再次退出。当不成功时,我完全跳过方法调用。
public void Update(double x, double y)
{
_updateLock.EnterWriteLock();
try
{ //...long running task... }
finally
{ _updateLock.ExitWriteLock(); }
}
//....
void Provider_PositionChanged(object sender, SpecialEventArgs e)
{
if (_updateLock.TryEnterWriteLock(0)) //noone is currently executing an update?
{
try { myUiControl.BeginInvoke(/*...*/); }
finally { _updateLock.ExitWriteLock(); }
}
这个方法可以工作,但是否有更优雅的解决方案呢?如何仅从一个线程简单地测试某个方法是否在任何(其他)线程上执行?
注意:使用Invoke()(而不是BeginInvoke())不是选项,因为这会阻止我的后台线程,从而防止其他东西在那里执行。
(1)这是一个地图解决方案MapXtreme,我想平移/缩放大型位图地形数据,并更新一些特性。
PS。这个问题有点相关,但涵盖了不同的方面:Winforms multithreading: Is creating a new delegate each time when invoking a method on the UI thread needed? 感谢任何答案!
更新:Hans Passant帮助了我,看到下面的解决方案。希望这也能帮助别人。
/// <summary>
/// This class enqueues asynchronously executing actions (that are running on another thread), but allows
/// to execute only one action at a time. When busy, newly enqueued actions are dropped.
/// Any enqueued action is required to call Done() on this when it has finished, to allow further actions
/// to execute afterwards.
/// </summary>
/// <remarks>This class is intended to help prevent stacking UI-Updates when the CPU or other resources
/// on the machine are not able to handle the amount of updates requested. However, the user
/// must keep in mind, that using this class may result
/// in dropped updates and that the last requested update is not always executed.</remarks>
public class ActionBouncer
{
/// <summary>
/// A event that signals the idle/busy state. Idle means, that no action is currently executing.
/// </summary>
private ManualResetEvent _idle = new ManualResetEvent(true);
/// <summary>
/// Enqueues the specified action, executing it when this bouncer
/// is currently idle.
/// </summary>
/// <param name="action">The action.</param>
public void Enqueue(Action action)
{
if (_idle.WaitOne(0)) //are we idle now? (Remark: This check and the reset below is not thread-safe (thanks to s.skov))
{
_idle.Reset(); //go to busy state
action(); //execute the action now.
}//else drop the action immediately.
}
/// <summary>
/// Signal the bouncer, that the currently executing asynchronous action is done, allowing
/// subsequent requests to execute.
/// This must get explicitly called (in code) at the end of the asynchronous action.
/// </summary>
public void Done()
{
_idle.Set();
}
}