创建新的UI线程并在原始线程上回调方法的最佳方法是什么?

时间:2019-06-27 15:31:06

标签: c# wpf multithreading user-interface asynchronous

我正在为第三方GUI应用程序编写一个插件类。应用程序调用了我的类的Run方法,并向我传递了供应商定义的Context对象。此Context对象中的任何方法或属性必须从当前线程(即应用程序的UI线程)中调用。

我的插件代码创建WPF Window并将其显示给用户。有些交互需要调用Context对象的方法,但是有些交互需要花费一些时间才能运行。当然,这会冻结我的UI。通常,我会从单独的线程中调用慢速方法,但是在这种情况下,由于Context对象与应用程序的UI线程相关联,因此我无法做到这一点。

这是我的Plugin类的示例实现:

public class Plugin
{
    public void Run(Context context)
    {
        // Create a window with my custom user interface
        var window = new MyWindow();

        // Call a slow method when MyButton is clicked
        window.MyButton.Click += (o, e) => context.SlowMethod();

        // Prevent exiting this Run method until the window is closed
        window.ShowDialog();
    }
}

有哪些可能的解决方案使我可以在具有响应式UI的情况下在Context对象(绑定到UI线程)中调用慢速方法?

我想出的一个解决方案是创建第二个线程,在该线程上运行我的UI,并使用原始线程调用Context方法。但是,我的实现非常复杂,因此我想知道是否有更简单的方法来实现这一目标。

这是我当前的解决方案:

public class Plugin
{
    public void Run(Context context)
    {
        // Get the application's UI thread dispatcher
        var dispatcher = Dispatcher.CurrentDispatcher;

        // Create a dispatcher frame to push later
        var frame = new DispatcherFrame();

        // Create a new UI thread (using an StaTaskScheduler)
        Task.Factory.StartNew(async () =>
        {
            var window = new MyWindow();

            // The Click event handler now uses the original
            // thread's dispatcher to run the slow method
            window.MyButton.Click += async (o, e) =>
                await dispatcher.InvokeAsync(() => context.SlowMethod());

            window.ShowDialog();

            // When the window is closed, end the dispatcher frame
            frame.Continue = false;
        }, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));

        // Prevent exiting this Run method until the frame is done
        Dispatcher.PushFrame(frame);
    }
}

StaTaskScheduler在STA线程中调度任务(根据WPF的要求)。它来自Parallel Extensions Extras库。

0 个答案:

没有答案