Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET:延迟后在UI线程上执行lambda的最佳方法?_C#_.net_Lambda - Fatal编程技术网

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

C# .NET:延迟后在UI线程上执行lambda的最佳方法?,c#,.net,lambda,C#,.net,Lambda,我遇到了一个情况,需要在延迟后在UI线程上运行lambda表达式。我想了好几种方法,最后决定采用这种方法 Task.Factory.StartNew(() => Thread.Sleep(1000)) .ContinueWith((t) => textBlock.Text="Done",TaskScheduler.FromCurrentSynchronizationContext()); 但我想知道是否有一种更简单的方法我错过了。对于更短、更简单或更简单的技术有什么建议吗?

我遇到了一个情况,需要在延迟后在UI线程上运行lambda表达式。我想了好几种方法,最后决定采用这种方法

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

但我想知道是否有一种更简单的方法我错过了。对于更短、更简单或更简单的技术有什么建议吗?假设.NET 4可用。

我认为最简单的方法是使用System.Windows.Forms.Timer,如果lambda不是某种随机函数的话

this._timer.Interval = 1000;
this._timer.Tick += (s, e) => this.textBlock.Text = "Done";
如果不需要在循环中执行labda,则添加此项

this.timer1.Tick += (s, e) => this.timer1.Stop();
打电话

this.timer1.Start();
在需要的地方

另一种方法是使用Invoke方法

delegate void FooHandler();

private void button1_Click(object sender, EventArgs e)
        {

            FooHandler handle = () =>  Thread.Sleep(1000); 
            handle.BeginInvoke(result => { ((FooHandler)((AsyncResult)result).AsyncDelegate).EndInvoke(result); this.textBox1.Invoke((FooHandler)(() => this.textBox1.Text = "Done")); }, null);
        }
Invoke保证委托将在UI线程中执行(其中存在父窗口主描述符)


可能存在更好的变体。

我认为你的产品相当不错

我认为一些人可能会遇到的唯一一个小问题是,为了执行延迟,您正在阻塞一个线程。当然,它是一个后台线程,除非您同时执行大量这些调用(每个调用占用一个线程),否则不太可能导致问题,但它仍然可能是次优的

相反,我建议您将算法考虑到实用方法中,并避免使用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));
    }
}
使用:

    UICallbackTimer.DelayExecution(TimeSpan.FromSeconds(1),
        () => textBlock.Text="Done");
当然,您也可以编写此DelayExecution方法的实现,该方法使用其他类型的计时器,如WPF Dispatchermer或WinForms timer类。我不确定这些不同计时器的权衡是什么。我猜Dispatchermer和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));
    }
}
和使用:

        SynchronizationContext.Current.Post(TimeSpan.FromSeconds(1),
            () => textBlock.Text="Done");

我喜欢你的扩展方法!这是一个干净的语法,避免了sleep()调用。唯一的缺点是需要向项目中添加静态扩展类,但这不是什么大问题。谢谢是否可以在执行延迟操作之前取消操作?SynchronizationContext.Current是WPF调度程序的好选择。如果您使用WPF,请使用