延迟的函数调用

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

例如

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 } 

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

如果有人好奇,这是必须的原因是foo()和bar()是在不同的(单例)类,我需要在特殊情况下相互调用。 问题是,这是在初始化完成,所以foo需要调用bar需要正在创build的foo类的实例…因此,延迟调用bar(),以确保foo完全instanciated ..读这回几乎是糟糕的devise!

编辑

我会在劝告下采取不良devise的观点! 我一直认为我可以改进这个系统,但是,这种恶劣的情况发生在抛出exception的情况下,在其他时候这两个单例并存非常好。 我认为我不会讨厌讨厌的asynchronous模式,而是要重构其中一个类的初始化。

这听起来像对这两个对象的创造的控制以及它们之间的相互依赖需要在外部进行控制,而不是在这些阶级之间进行控制。

我一直在寻找这样的东西我自己 – 我想出了以下,虽然它使用了一个计时器,它使用它只有一次的初始延迟,并不需要任何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 } 

(感谢Fred Deschenes在callback中configuration定时器的想法)

感谢现代C#5/6 🙂

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

除了同意以前评论者的devise观察外,没有任何解决scheme对我来说是足够干净的。 .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 (); } } 

对于上下文,我正在使用这个去除一个ICommand绑在一个UI元素上的鼠标左键。 用户双击是造成各种破坏。 (我知道我也可以使用Click / DoubleClick处理程序,但是我想要一个可以与ICommand工作的解决scheme)。

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

这确实是一个非常糟糕的devise,更不用说单身单身本身就是糟糕的devise。

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

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

然而,这将在单独的线程上调用bar() 。 如果您需要在原始线程中调用bar() ,则可能需要将bar()调用移动到RunWorkerCompleted处理函数,或者使用SynchronizationContext进行一些黑客SynchronizationContext

 public static class DelayedDelegate { static Timer runDelegates; static Dictionary<MethodInvoker, DateTime> delayedDelegates = new Dictionary<MethodInvoker, DateTime>(); static DelayedDelegate() { runDelegates = new Timer(); runDelegates.Interval = 250; runDelegates.Tick += RunDelegates; runDelegates.Enabled = true; } public static void Add(MethodInvoker method, int delay) { delayedDelegates.Add(method, DateTime.Now + TimeSpan.FromSeconds(delay)); } static void RunDelegates(object sender, EventArgs e) { List<MethodInvoker> removeDelegates = new List<MethodInvoker>(); foreach (MethodInvoker method in delayedDelegates.Keys) { if (DateTime.Now >= delayedDelegates[method]) { method(); removeDelegates.Add(method); } } foreach (MethodInvoker method in removeDelegates) { delayedDelegates.Remove(method); } } } 

用法:

 DelayedDelegate.Add(MyMethod,5); void MyMethod() { MessageBox.Show("5 Seconds Later!"); } 

那么,我不得不同意“devise”的观点…但是你可以使用监视器让对方知道什么时候过了关键部分…

  public void foo() { // Do stuff! object syncLock = new object(); lock (syncLock) { // Delayed call to bar() after x number of ms ThreadPool.QueueUserWorkItem(delegate { lock(syncLock) { bar(); } }); // Do more Stuff } // lock now released, bar can begin } 

我虽然完美的解决办法是有一个计时器处理延迟的行动。 当你有一个小于一秒的间隔FxCop不喜欢。 我需要延迟我的行动,直到我的DataGrid按列完成sorting。 我认为一次性计时器(AutoReset = false)将是解决scheme,并且完美地工作。 而且,FxCop不会让我压制这个警告!

除了使用定时器和事件之外,没有标准的方法来延迟对函数的调用。

这听起来像延迟调用方法的GUI反模式,以便您可以确定表单已经完成布局。 不是一个好主意。

基于David O'Donoghue的回答,这里是Delayed Delegate的优化​​版本:

 using System.Windows.Forms; using System.Collections.Generic; using System; namespace MyTool { public class DelayedDelegate { static private DelayedDelegate _instance = null; private Timer _runDelegates = null; private Dictionary<MethodInvoker, DateTime> _delayedDelegates = new Dictionary<MethodInvoker, DateTime>(); public DelayedDelegate() { } static private DelayedDelegate Instance { get { if (_instance == null) { _instance = new DelayedDelegate(); } return _instance; } } public static void Add(MethodInvoker pMethod, int pDelay) { Instance.AddNewDelegate(pMethod, pDelay * 1000); } public static void AddMilliseconds(MethodInvoker pMethod, int pDelay) { Instance.AddNewDelegate(pMethod, pDelay); } private void AddNewDelegate(MethodInvoker pMethod, int pDelay) { if (_runDelegates == null) { _runDelegates = new Timer(); _runDelegates.Tick += RunDelegates; } else { _runDelegates.Stop(); } _delayedDelegates.Add(pMethod, DateTime.Now + TimeSpan.FromMilliseconds(pDelay)); StartTimer(); } private void StartTimer() { if (_delayedDelegates.Count > 0) { int delay = FindSoonestDelay(); if (delay == 0) { RunDelegates(); } else { _runDelegates.Interval = delay; _runDelegates.Start(); } } } private int FindSoonestDelay() { int soonest = int.MaxValue; TimeSpan remaining; foreach (MethodInvoker invoker in _delayedDelegates.Keys) { remaining = _delayedDelegates[invoker] - DateTime.Now; soonest = Math.Max(0, Math.Min(soonest, (int)remaining.TotalMilliseconds)); } return soonest; } private void RunDelegates(object pSender = null, EventArgs pE = null) { try { _runDelegates.Stop(); List<MethodInvoker> removeDelegates = new List<MethodInvoker>(); foreach (MethodInvoker method in _delayedDelegates.Keys) { if (DateTime.Now >= _delayedDelegates[method]) { method(); removeDelegates.Add(method); } } foreach (MethodInvoker method in removeDelegates) { _delayedDelegates.Remove(method); } } catch (Exception ex) { } finally { StartTimer(); } } } } 

通过为代表使用一个唯一的密钥,class级可以稍微改进一点。 因为如果您在第一次触发之前再次添加相同的委托,则可能会遇到字典问题。

 private static volatile List<System.Threading.Timer> _timers = new List<System.Threading.Timer>(); private static object lockobj = new object(); public static void SetTimeout(Action action, int delayInMilliseconds) { System.Threading.Timer timer = null; var cb = new System.Threading.TimerCallback((state) => { lock (lockobj) _timers.Remove(timer); timer.Dispose(); action() }); lock (lockobj) _timers.Add(timer = new System.Threading.Timer(cb, null, delayInMilliseconds, System.Threading.Timeout.Infinite)); }