延迟功能调用

TK.*_*TK. 80 c# function delay

是否有一个很好的简单方法来延迟函数调用,同时让线程继续执行?

例如

public void foo()
{
    // Do stuff!

    // Delayed call to bar() after x number of ms

    // Do more Stuff
}

public void bar()
{
    // Only execute once foo has finished
}
Run Code Online (Sandbox Code Playgroud)

我知道这可以通过使用计时器和事件处理程序来实现,但我想知道是否有一种标准的c#方式来实现这一目标?

如果有人好奇,那么需要的原因是foo()和bar()在不同的(单例)类中,我需要在特殊情况下相互调用.问题是这是在初始化时完成的,所以foo需要调用bar,它需要一个正在创建的foo类的实例...因此延迟调用bar()以确保foo完全实例化.读回来几乎是糟糕的设计!

编辑

我会在建议下接受关于糟糕设计的观点!我一直认为我可能能够改进系统,但是,这种令人讨厌的情况只会在抛出异常时发生,而在其他所有其他时候两个单身人士共存非常好.我认为我不会讨厌令人讨厌的异步模式,而是我要重构其中一个类的初始化.

Kor*_*yem 143

感谢现代C#5/6 :)

public void foo()
{
    Task.Delay(1000).ContinueWith(t=> bar());
}

public void bar()
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案很棒,有两个原因.代码简单性和Delay不创建线程也不像其他Task.Run或Task.StartNew那样使用线程池的事实......它在内部是一个计时器. (13认同)
  • 另请注意稍微清洁(IMO)的等效版本:Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => bar()); (4认同)
  • 以下是在 UI 线程上继续的方法: Task.Delay(1000).ContinueWith(_ => { Application.Current.Dispatcher.Invoke(() => { bar(); }); (4认同)
  • @Zyo实际上它确实使用了不同的线程.尝试从中访问UI元素,它将触发异常. (3认同)

dod*_*der 92

我一直在寻找类似的东西 - 我想出了以下内容,虽然它确实使用了计时器,它只使用一次初始延迟,并且不需要任何Sleep调用......

public void foo()
{
    System.Threading.Timer timer = null; 
    timer = new System.Threading.Timer((obj) =>
                    {
                        bar();
                        timer.Dispose();
                    }, 
                null, 1000, System.Threading.Timeout.Infinite);
}

public void bar()
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

(感谢Fred Deschenes关于在回调中处理计时器的想法)

  • @dodgy_coder错了.使用绑定到委托对象`cb`的lambda中的`timer`局部变量使它被提升到anon存储(闭包实现细节)上,这将导致`Timer`对象从GC的角度可以访问只要`TimerCallback`委托本身可以访问.换句话说,'Timer`对象是**保证**,直到线程池调用委托对象之后才进行垃圾收集. (5认同)
  • 我觉得这是延迟函数调用的最佳答案.没有线程,没有后台工作,没有睡眠.定时器非常高效,内存/ CPU也很明智. (2认同)

cod*_*k3y 15

除了同意之前评论者的设计观察结果外,没有一个解决方案对我来说足够干净..Net 4提供了使当前线程上的延迟执行非常简单的类DispatcherTask类:

static class AsyncUtils
{
    static public void DelayCall(int msec, Action fn)
    {
        // Grab the dispatcher from the current executing thread
        Dispatcher d = Dispatcher.CurrentDispatcher;

        // Tasks execute in a thread pool thread
        new Task (() => {
            System.Threading.Thread.Sleep (msec);   // delay

            // use the dispatcher to asynchronously invoke the action 
            // back on the original thread
            d.BeginInvoke (fn);                     
        }).Start ();
    }
}
Run Code Online (Sandbox Code Playgroud)

对于上下文,我正在使用它ICommand在UI元素上去除绑定到鼠标左键.用户双击导致各种破坏.(我知道我也可以使用Click/ DoubleClick处理程序,但我想要一个可以全面使用的解决方案ICommand).

public void Execute(object parameter)
{
    if (!IsDebouncing) {
        IsDebouncing = true;
        AsyncUtils.DelayCall (DebouncePeriodMsec, () => {
            IsDebouncing = false;
        });

        _execute ();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*lph 7

这听起来像控制这些对象的创建和它们的相互依赖需要在外部控制,而不是在类本身之间.


Ant*_*lev 5

这确实是一个非常糟糕的设计,更不用说单身人士本身就是糟糕的设计.

但是,如果您确实需要延迟执行,那么您可以执行以下操作:

BackgroundWorker barInvoker = new BackgroundWorker();
barInvoker.DoWork += delegate
    {
        Thread.Sleep(TimeSpan.FromSeconds(1));
        bar();
    };
barInvoker.RunWorkerAsync();
Run Code Online (Sandbox Code Playgroud)

但是,这将bar()在一个单独的线程上调用.如果你需要调用bar()原始线程,你可能需要将bar()调用移动到RunWorkerCompleted处理程序或做一些黑客攻击SynchronizationContext.