使用的SynchronizationContext用于发送事件回到用户界面的WinForms或WPF [英] Using SynchronizationContext for sending events back to the UI for WinForms or WPF

查看:284
本文介绍了使用的SynchronizationContext用于发送事件回到用户界面的WinForms或WPF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的SynchronizationContext元帅事件回从我的DLL,它做了很多的多线程后台任务的UI线程。

I'm using a SynchronizationContext to marshal events back to the UI thread from my DLL that does a lot of multi-threaded background tasks.

我知道Singleton模式是不是最喜欢的,但我使用它现在存储用户界面的的SynchronizationContext的参考,当您创建Foo的父对象。

I know the singleton pattern isn't a favorite, but I'm using it for now to store a reference of the UI's SynchronizationContext when you create foo's parent object.

public class Foo
{
	public event EventHandler FooDoDoneEvent;

	public void DoFoo()
	{
		//stuff
		OnFooDoDone();
	}

	private void OnFooDoDone()
	{
		if (FooDoDoneEvent != null)
		{
			if (TheUISync.Instance.UISync != SynchronizationContext.Current)
			{
				TheUISync.Instance.UISync.Post(delegate { OnFooDoDone(); }, null);
			}
			else
			{
				FooDoDoneEvent(this, new EventArgs());
			}
		}

	}
}

这并没有在所有的工作在WPF中,TheUISync情况下,用户界面​​同步(从主窗口饲料)永远不匹配当前SynchronizationContext.Current。在窗口的形式,当我做同样的事情,他们会在调用匹配后,我们会尽快给正确的线程。

This didn't work at all in WPF, the TheUISync instances UI sync (which is feed from the main window) never matches the current SynchronizationContext.Current. In windows form when I do the same thing they will match after an invoke and we'll get back to the correct thread.

我的修正,我恨,看起来像

My fix, which i hate, looks like

public class Foo
{
	public event EventHandler FooDoDoneEvent;

	public void DoFoo()
	{
		//stuff
		OnFooDoDone(false);
	}

	private void OnFooDoDone(bool invoked)
	{
		if (FooDoDoneEvent != null)
		{
			if ((TheUISync.Instance.UISync != SynchronizationContext.Current) && (!invoked))
			{
				TheUISync.Instance.UISync.Post(delegate { OnFooDoDone(true); }, null);
			}
			else
			{
				FooDoDoneEvent(this, new EventArgs());
			}
		}

	}
}

所以,我希望这样做足够的理智来效仿。

So I hope this sample makes enough sense to follow.

推荐答案

眼前的问题

您眼前的问题是, SynchronizationContext.Current 不自动为WPF设置。要设置WPF下运行时,你需要做这样的事情在你的TheUISync code:

Your immediate problem is that SynchronizationContext.Current is not automatically set for WPF. To set it you will need to do something like this in your TheUISync code when running under WPF:

var context = new DispatcherSynchronizationContext(
                    Application.Current.Dispatcher);
SynchronizationContext.SetSynchronizationContext(context);
UISync = context;

一个更深层次的问题

的SynchronizationContext 是并列的COM +的支持,旨在跨线程。在WPF中你不能有一个调度跨越多个线程,因此一个的SynchronizationContext 不能真正跨线程。有许多的场景中,一个的SynchronizationContext 可以切换到一个新的话题 - 特别是其中的任何要求 ExecutionContext.Run()。所以,如果你使用的是的SynchronizationContext 来提供事件双方的WinForms和WPF客户端,你需要注意,某些情况下会分解,例如Web请求到Web服务或网站托管在同一进程将是一个问题。

SynchronizationContext is tied in with the COM+ support and is designed to cross threads. In WPF you cannot have a Dispatcher that spans multiple threads, so one SynchronizationContext cannot really cross threads. There are a number of scenarios in which a SynchronizationContext can switch to a new thread - specifically anything which calls ExecutionContext.Run(). So if you are using SynchronizationContext to provide events to both WinForms and WPF clients, you need to be aware that some scenarios will break, for example a web request to a web service or site hosted in the same process would be a problem.

如何解决需要的SynchronizationContext

由于这个原因,我建议使用WPF的调度机制专门用于这一目的,甚至与WinForms的code。您已经创建了存储同步的TheUISync单例类,让您明明白白有某种方式挂接到应用程序的顶层。但是你这样做,你可以添加code创造增加了一些WPF内容到你的WinForms应用程序,以便调度将工作,然后使用新的调度的机制,我在下面说明。

Because of this I suggest using WPF's Dispatcher mechanism exclusively for this purpose, even with WinForms code. You have created a "TheUISync" singleton class that stores the synchronization, so clearly you have some way to hook into the top level of the application. However you are doing so, you can add code which creates adds some WPF content to your WinForms application so that Dispatcher will work, then use the new Dispatcher mechanism which I describe below.

使用调度代替的SynchronizationContext

WPF的调度机制实际上不再需要一个单独的的SynchronizationContext 对象。除非你有一定的互操作方案,例如共享code与COM +对象或WinForms的用户界面,你最好的解决方案是使用调度,而不是的SynchronizationContext

WPF's Dispatcher mechanism actually eliminates the need for a separate SynchronizationContext object. Unless you have certain interop scenarios such sharing code with COM+ objects or WinForms UIs, your best solution is to use Dispatcher instead of SynchronizationContext.

这是这样的:

public class Foo 
{ 
  public event EventHandler FooDoDoneEvent; 

  public void DoFoo() 
  { 
    //stuff 
    OnFooDoDone(); 
  } 

  private void OnFooDoDone() 
  { 
    if(FooDoDoneEvent!=null)
      Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal, new Action(() =>
        {
          FooDoDoneEvent(this, new EventArgs()); 
        }));
  }
}

请注意,您不再需要TheUISync对象 - WPF处理,详细说明您

Note that you no longer need a TheUISync object - WPF handles that detail for you.

如果你更舒适与老代理语法,你可以这样做的,而不是:

If you're more comfortable with the older delegate syntax you can do it that way instead:

      Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal, new Action(delegate
        {
          FooDoDoneEvent(this, new EventArgs()); 
        }));

一个无关的错误修复

另外请注意,还有一个就是这里复制到你原来的code的错误。问题是,FooDoneEvent可设定时间OnFooDoDone被称为与时间之间空的的BeginInvoke (或发布在原来的code)调用的委托。解决方法是委托内部的第二个测试:

Also note that there is a bug in your original code that is replicated here. The problem is that FooDoneEvent can be set to null between the time OnFooDoDone is called and the time the BeginInvoke (or Post in the original code) calls the delegate. The fix is a second test inside the delegate:

    if(FooDoDoneEvent!=null)
      Application.Current.Dispatcher.BeginInvoke(
        DispatcherPriority.Normal, new Action(() =>
        {
          if(FooDoDoneEvent!=null)
            FooDoDoneEvent(this, new EventArgs()); 
        }));

这篇关于使用的SynchronizationContext用于发送事件回到用户界面的WinForms或WPF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆