【问题标题】:Portable class library equivalent of Dispatcher.Invoke or Dispatcher.RunAsync等效于 Dispatcher.Invoke 或 Dispatcher.RunAsync 的可移植类库
【发布时间】:2012-06-29 08:00:59
【问题描述】:

在 .NET、Windows 8 和 Windows Phone 7 中,我有类似这样的代码:

public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
    if (dispatcher.CheckAccess())
    {
        action();
    }
    else
    {
        dispatcher.Invoke(action);
    }
}

我将如何在可移植类库中做一些事情?最好有一个平台无关的实现。我的想法是使用 TPL,它在 WP7 中不可用,但肯定很快就会出现。

// PortableDispatcher must be created on the UI thread and then made accessible 
// maybe as a property in my ViewModel base class.
public class PortableDispatcher
{
    private TaskScheduler taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

    public void Invoke(Action action)
    {
        if (Alread on UI thread. How would I do this.)
        {
            action();
        }

        Task.Factory.StartNew(
            action, 
            CancellationToken.None,
            TaskCreationOptions.None,
            taskScheduler);
    }
}

我唯一不确定的是这会对性能产生什么影响。也许我会做一些测试。

【问题讨论】:

    标签: c# .net task-parallel-library dispatcher portable-class-library


    【解决方案1】:

    您可以使用SynchronizationContext.Post 或发送方法。它是可移植的,当您使用带有调度程序的 UI 框架时,当前同步上下文会将工作委托给调度程序。

    具体可以使用如下代码:

    void InvokeIfRequired(this SynchroniationContext context, Action action)
    {
        if (SynchroniationContext.Current == context)
        {
            action();
        }
        else
        {
            context.Send(action) // send = synchronously
            // context.Post(action)  - post is asynchronous.
        }  
    }
    

    【讨论】:

    • 不错的一个。您将如何创建我在上面为调度程序使用的扩展方法。具体来说,您如何检查您是否已经在 UI 线程上运行而不是执行 dispatcher.CheckAccess()。
    • 也是 Post BeginInvoke 的等价物和 Send Invoke 的等价物?
    • 发帖前无需检查。 Post 已经进行了适当的检查。
    • 请注意,Metro 风格的应用程序不支持 SynchronizationContext.Send - 您将收到 NotSupportedException。因此,该方法现在在可移植类库中被标记为过时。见msdn.microsoft.com/en-us/library/…
    • context.Post 请求 SendOrPostCallback
    【解决方案2】:

    随着 TPL 的出现。我想出了一个稍微改进的接受答案版本,它返回一个任务,可以使用新的 async 和 await 关键字等待。

    public Task RunAsync(Action action)
    {
        TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();
    
        if (this.synchronizationContext == SynchronizationContext.Current)
        {
            try
            {
                action();
                taskCompletionSource.SetResult(null);
            }
            catch (Exception exception)
            {
                taskCompletionSource.SetException(exception);
            }
        }
        else
        {
            // Run the action asyncronously. The Send method can be used to run syncronously.
            this.synchronizationContext.Post(
                (obj) => 
                {
                    try
                    {
                        action();
                        taskCompletionSource.SetResult(null);
                    }
                    catch (Exception exception)
                    {
                        taskCompletionSource.SetException(exception);
                    }
                }, 
                null);
        }
    
        return taskCompletionSource.Task;
    }
    

    【讨论】:

    • 不错。看起来您创建了一个类,但只发布了该方法,因此(可能很明显)为其他人提供了注释。 this.synchronizationContext 必须在调用方法之前在您希望操作运行的线程中分配。我实际上在上面做了一点修改并创建了一个扩展:public Task RunAsync(this SynchronizationContext context, Action action)。现在它更像是原始答案,因为它可以独立存在。两个答案中最好的。 :)
    猜你喜欢
    • 1970-01-01
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    • 2016-09-14
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    相关资源
    最近更新 更多