1

私は以下のコードに取り組んでおり、できるだけ速くしようとしています。

基本的に、システムでイベントがトリガーされるたびに execute メソッドが呼び出されます。私がテストしているのは、reduce が最後に実行されてから x 分が経過したかどうかを確認することです。x 分が経過した場合は、タスクを実行する必要があります。

イベントは任意のスレッドからトリガーされ、非常に迅速に発生する可能性があるため、ロックの外側でタスクをトリガーする方が (タスクであっても)、ロック内にあるよりも優れていると考えました。

これを改善する方法についてフィードバックはありますか?

public class TriggerReduce
{ 
    private readonly object _lock = new object();
    private readonly int _autoReduceInterval = 5;
    private DateTime _lastTriggered;

    public void Execute(object sender, EventArgs e)
    {
        var currentTime = DateTime.Now;
        if (currentTime.Subtract(_lastTriggered).Duration().TotalMinutes > _autoReduceInterval)
        {
            var shouldRun = false;
            lock (_lock)
            {
                if (currentTime.Subtract(_lastTriggered).Duration().TotalMinutes > _autoReduceInterval)
                {
                    _lastTriggered = currentTime;
                    shouldRun = true;
                }
            }

            if (shouldRun)
            {
                Task.Factory.StartNew(() =>
                {
                    //Trigger reduce which is a long running task
                }, TaskCreationOptions.LongRunning);
            }
        }
    }
}
4

5 に答える 5

1

ああ、私はそれをしません !「if (currentTime」と「shouldRun」をロック内に戻します。

ロックの外で状態を変更/チェックしないでください - それは確実に失敗します。

この場合、'shouldRun' を true に設定したばかりのスレッドは、別のスレッドが入って 'shouldRun' を再び false に設定した後、ロックでスタックする前に、その決定を覆す可能性があります。最初のスレッドが _lastTriggered を現在の時刻に設定したため、最初のスレッドは 'StartNew' に到達せず、後のスレッドも到達しません。

OTOH :)「shouldRun」はフィールドではなく自動変数であるため、状態ではありません。1 つのスレッドのみがロック内に入り、間隔を再確認して _lastTriggered 時間を更新できます。

この種の再確認は好きではありませんが、現時点では、なぜ機能しないのかわかりません。

于 2012-04-09T10:22:58.580 に答える
0

ロックを回避し、代わりにInterlocked.Exchangeを使用すると便利ですか?

例えば

private long _lastTriggeredTicks;

private DateTime lastTriggered
{
    get 
    { 
        var l = Interlocked.Read( ref _lastTriggeredTicks );
        return new DateTime( l );
    }
    set
    {
        Interlocked.Exchange( ref _lastTriggeredTicks, value );
    }
}

私が理解していることから、ステートメントInterlockedよりも速いです。lock

于 2012-04-09T10:26:39.213 に答える
0
public class TriggerReduce //StartNew is fast and returns fast
{

    private readonly object _lock = new object();
    private readonly int _triggerIntervalMins = 5;

    private DateTime _nextTriggerAt = DateTime.MinValue;
    private bool inTrigger = false;

    public void Execute(object sender, EventArgs e)
    {
        lock (_lock)
        {
            var currentTime = DateTime.Now;
            if (_nextTriggerAt > currentTime)
                return;

            _nextTriggerAt = currentTime.AddMinutes(_triggerIntervalMins);//runs X mins after last task started running (or longer if task took longer than X mins)
        }

        Task.Factory.StartNew(() =>
        {
            //Trigger reduce which is a long running task
        }, TaskCreationOptions.LongRunning);
    }
}


public class TriggerReduce//startNew is a long running function that you want to wait before you recalculate next execution time
{

    private readonly object _lock = new object();
    private readonly int _triggerIntervalMins = 5;

    private DateTime _nextTriggerAt = DateTime.MinValue;
    private bool inTrigger = false;

    public void Execute(object sender, EventArgs e)
    {
        var currentTime;
        lock (_lock)
        {
            currentTime = DateTime.Now;
            if (inTrigger || (_nextTriggerAt > currentTime))
                return;

            inTrigger = true;
        }
        Task.Factory.StartNew(() =>
        {
            //Trigger reduce which is a long running task
        }, TaskCreationOptions.LongRunning);

        lock (_lock)
        {
            inTrigger = false;
            _nextTriggerAt = DateTime.Now.AddMinutes(_triggerIntervalMins);//runs X mins after task finishes
            //_nextTriggerAt = currentTime.AddMinutes(_triggerIntervalMins);//runs X mins after last task started running (or longer if task took longer than X mins)
        }
    }
}
于 2012-04-09T10:27:45.597 に答える
0

あなたはすでにかなり合理的なアプローチをとっていると思います。_lastTriggered大きな問題は、ロックの外側にアクセスしていることです。二重チェックのロックのイディオムは、ここでは機能しません。単純にコードを次のようにします。

public void Execute(object sender, EventArgs e)
{
    var currentTime = DateTime.Now;
    var shouldRun = false;
    lock (_lock)
    {
        TimeSpan span = currentTime - _lastTriggeed;
        if (span.TotalMinutes > _autoReduceInterval)
        {
            _lastTriggered = currentTime;
            shouldRun = true;
        }
    }

    if (shouldRun)
    {
        Task.Factory.StartNew(() =>
        {
            //Trigger reduce which is a long running task
        }, TaskCreationOptions.LongRunning);
    }
}
于 2012-04-09T13:16:55.100 に答える
0

Monitor.TryEnter を使用します。

if (Monitor.TryEnter(_lock))
{
    try
    {
        if (currentTime.Subtract(_lastTriggered).Duration().TotalMinutes >
            _autoReduceInterval)
        {
            _lastTriggered = currentTime;
            shouldRun = true;
        } 
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}
于 2012-04-09T10:21:02.077 に答える