Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
794 views
in Technique[技术] by (71.8m points)

async await - C#: How to implement keep alive timers for many objects in a proper and efficient way?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is a way to keep track of the expiration status of an object passively, without using timers. The last time that each object was used is stored in a private double field, and this field is updated every time the object is used. In case the object has not been used for a long time, the field will take the value double.MaxValue which means "expired", and will keep this value forever. The GetExpired method below handles the complexity of comparing and updating the field atomically and with thread-safety:

public static bool GetExpired(ref double lastUsed, TimeSpan slidingExpiration,
    bool touch)
{
    // Magic values, 0: Not initialized, double.MaxValue: Expired
    double previous = Volatile.Read(ref lastUsed);
    if (previous == double.MaxValue) return true;

    // Get current timestamp in seconds
    double now = (double)Stopwatch.GetTimestamp() / Stopwatch.Frequency;
    if (previous == 0D || now - previous < slidingExpiration.TotalSeconds)
    {
        // Not expired (unless preempted)
        if (!touch) return false;
        var original = Interlocked.CompareExchange(ref lastUsed, now, previous);
        return original == double.MaxValue;
        // In any other case that original != previous we've lost the race to update
        // the field, but its value should be very close to 'now'. So not expired.
    }
    else
    {
        // Expired (unless preempted)
        var original = Interlocked.CompareExchange(ref lastUsed, double.MaxValue,
            previous);
        return original == double.MaxValue || original == previous;
    }
}

Usage example:

public class MyComObject
{
    private readonly TimeSpan _slidingExpiration = TimeSpan.FromSeconds(60);
    private double _lastUsed;

    public MyComObject() // Constructor
    {
        GetExpired(ref _lastUsed, default, touch: true); // Start expiration "timer"
    }

    public bool IsExpired => GetExpired(ref _lastUsed, _slidingExpiration, touch: false);

    public bool TryDoSomething()
    {
        if (GetExpired(ref _lastUsed, _slidingExpiration, touch: true)) return false;
        //...
        return true; // The job was done
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...