.NET:延迟后在UI线程上执行lambda的最佳方法?

Sco*_*ger 14 .net c# lambda

我遇到了一个需要在延迟后在UI线程上运行lambda表达式的情况.我想到了几种方法,并最终确定了这种方法

Task.Factory.StartNew(() => Thread.Sleep(1000))
    .ContinueWith((t) => textBlock.Text="Done",TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

但我想知道是否有一种更容易错过的方法.对更短,更简单或更简单的技术的任何建议?假设.NET 4可用.

Phi*_*hil 22

我觉得你所拥有的是非常好的斯科特.

我认为有些人可能会遇到的唯一一个小问题是,你要阻止一个线程来执行你的延迟.当然它是一个后台线程,除非你同时执行大量的这些调用(每个都绑定一个线程),否则不太可能导致问题,但它仍然可能不是最理想的.

我建议您将算法分解为实用方法,并避免使用Thread.Sleep.

显然可能有无数种方法可以做到这一点,但这里有一个:

public static class UICallbackTimer
{
    public static void DelayExecution(TimeSpan delay, Action action)
    {
        System.Threading.Timer timer = null;
        SynchronizationContext context = SynchronizationContext.Current;

        timer = new System.Threading.Timer(
            (ignore) =>
            {
                timer.Dispose();

                context.Post(ignore2 => action(), null);
            }, null, delay, TimeSpan.FromMilliseconds(-1));
    }
}
Run Code Online (Sandbox Code Playgroud)

使用:

    UICallbackTimer.DelayExecution(TimeSpan.FromSeconds(1),
        () => textBlock.Text="Done");
Run Code Online (Sandbox Code Playgroud)

当然,您也可以编写此DelayExecution方法的实现,该方法使用其他类型的计时器,例如WPF DispatcherTimer或WinForms Timer类.我不确定这些不同计时器的权衡取舍.我的猜测是DispatcherTimer和WinForm的计时器实际上仍然可以在相反类型的应用程序上运行.

编辑:

重新阅读我的答案,我认为实际上我很想将其纳入一个适用于同步上下文的扩展方法 - 如果你考虑一下,更一般的说法就是你需要能够将工作发布回到一段延迟后的同步上下文.

SynchronizationContext已经有一个用于排队工作的post方法,原始调用者不希望在完成时阻塞.我们需要的是一个版本,在延迟后发布工作,所以相反:

public static class SyncContextExtensions
{
    public static void Post(this SynchronizationContext context, TimeSpan delay, Action action)
    {
        System.Threading.Timer timer = null;

        timer = new System.Threading.Timer(
            (ignore) =>
            {
                timer.Dispose();

                context.Post(ignore2 => action(), null);
            }, null, delay, TimeSpan.FromMilliseconds(-1));
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用:

        SynchronizationContext.Current.Post(TimeSpan.FromSeconds(1),
            () => textBlock.Text="Done");
Run Code Online (Sandbox Code Playgroud)