可移植类库中与Dispatcher.Invoke或Dispatcher.RunAsync等价的方法

10

在 .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);
    }
}

我唯一不确定的是这样做的性能影响如何,也许我会进行一些测试。

2个回答

14
您可以使用 SynchronizationContext.Post 或 Send 方法。它是可移植的,当您在使用带有 Dispatcher 的 UI 框架时,当前同步上下文将把工作委托给 Dispatcher。
具体而言,您可以使用以下代码:
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()。 - Muhammad Rehan Saeed
1
您无需在发布前进行检查。Post已经执行了适当的检查。 - Panagiotis Kanavos
看起来不像。反射器显示如下:public virtual void Post(SendOrPostCallback d, object state) { ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state); } - Muhammad Rehan Saeed
4
请注意,在 Metro 应用中不支持 SynchronizationContext.Send 方法-您将会收到 NotSupportedException 异常。因此,该方法现在已被标记为过时的可移植类库。请参阅 http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.send(v=vs.110).aspx。 - Daniel Plaisted
1
context.Post要求一个SendOrPostCallback。 - tofutim
显示剩余2条评论

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;
}

2
不错。看起来你创建了一个类,但只发布了方法,所以对于其他人来说(可能是显而易见的)需要注意一下。在调用该方法之前,必须在要运行操作的线程中分配this.synchronizationContext。我实际上稍微修改了上面的内容并创建了一个扩展:public Task RunAsync(this SynchronizationContext context, Action action)。现在它更像原始答案,可以独立存在。两个答案的最佳结合。 :) - Wes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接