104

スレッドの実行を継続させながら関数呼び出しを遅らせる簡単な方法はありますか?

例えば

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 を foo が呼び出す必要があることです。悪いデザインのほとんどピシャリ!

編集

悪いデザインの指摘はアドバイスで受けます!私はシステムを改善できるかもしれないと長い間考えてきましたが、この厄介な状況は例外がスローされたときにのみ発生し、それ以外の場合は常に 2 つのシングルトンが非常にうまく共存しています。私は厄介な非同期パターンで混乱するつもりはないと思います。むしろ、クラスの 1 つの初期化をリファクタリングするつもりです。

4

12 に答える 12

103

私は自分でこのようなものを探していました-次のことを思いつきましたが、タイマーを使用しますが、最初の遅延のために一度だけ使用し、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に感謝します)

于 2011-07-11T06:12:22.300 に答える
2
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!");
}
于 2012-01-10T15:06:30.363 に答える
1

完璧な解決策は、遅延アクションをタイマーで処理することです。FxCop は、間隔が 1 秒未満の場合を好みません。DataGrid が列による並べ替えを完了するまで、アクションを遅らせる必要があります。ワンショット タイマー (AutoReset = false) が解決策になると考えましたが、それは完全に機能します。そして、FxCop では警告を抑制できません!

于 2012-01-26T14:36:31.413 に答える
0
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));
}
于 2014-01-31T16:18:58.377 に答える
0

David O'Donoghue からの回答に基づいて構築されたのが、遅延デリゲートの最適化されたバージョンです。

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

デリゲートに一意のキーを使用することで、クラスをさらに改善できます。最初のデリゲートが起動する前に同じデリゲートを 2 回追加すると、ディクショナリに問題が発生する可能性があるためです。

于 2013-09-27T13:19:19.490 に答える