Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#:如何以适当有效的方式为许多对象实现保持活动的计时器?_C#_Async Await_Timer_Task - Fatal编程技术网

C#:如何以适当有效的方式为许多对象实现保持活动的计时器?

C#:如何以适当有效的方式为许多对象实现保持活动的计时器?,c#,async-await,timer,task,C#,Async Await,Timer,Task,我想实现的很容易解释,但因为有这么多不同的可能性,我并不真正了解每种可能方法的利弊: 在我的应用程序中有很多(比如说数千个通信对象)。 当这样的对象空闲一段时间(意味着某些方法没有被调用)时,它应该被简单地关闭(这里详细的意思与此无关) 我想到了一个与每个对象相关联的“计时器”,每当我“使用”对象时,它都会被重新触发。比如: public void ReTrigger() { lock (_some_locking) { //Reset the timer

我想实现的很容易解释,但因为有这么多不同的可能性,我并不真正了解每种可能方法的利弊:

在我的应用程序中有很多(比如说数千个通信对象)。 当这样的对象空闲一段时间(意味着某些方法没有被调用)时,它应该被简单地关闭(这里详细的意思与此无关)

我想到了一个与每个对象相关联的“计时器”,每当我“使用”对象时,它都会被重新触发。比如:

public void ReTrigger()
{
    lock (_some_locking)
    {
            //Reset the timer
            _timer.Stop();
            _timer.Start();
        }
    }
}
注意,我的应用程序大量使用async/await,我希望使用最适合这个概念的解决方案。我想避免为了运行大量计时器而增加大量线程

有许多不同的计时器可用:

System.Timers.Timer
System.Threading.Timer
System.Windows.Forms.Timer
System.Web.UI.Timer
System.Windows.Threading.DispatcherTimer
那么,哪一个“适合”我使用asyncio的概念呢

换言之,依赖像这样的背景任务会更好吗

while (true)
{
     try
     {
        await Task.Delay(timeout, _cancellationToken)
        ... some action when expired ...
     }
     catch (TaskCanceledException)
     {
        // we have been re-triggered by "using" the object
     }
}
       
这更符合我的概念,但是,在这种情况下,每次重新触发后我都需要一个新的取消令牌,这不是很好

解决我的问题的“黄金之路”是什么;最好使用异步任务


另一个解决方案是内务管理任务,它周期性地轮询所有活动对象是否过期。这只需要一个正在运行的计时器,但也不是很好。

下面是一种被动跟踪对象过期状态的方法,无需使用计时器。每个对象上次使用的时间存储在私有
double
字段中,该字段在每次使用该对象时都会更新。如果对象长时间未使用,该字段将采用值
double.MaxValue
,表示“过期”,并将永久保留该值。下面的
GetExpired
方法处理以原子方式和线程安全性比较和更新字段的复杂性:

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

你不能用过期策略将它们存储在内存缓存中,并在使用时更新吗?当通信对象关闭时,你想在那一刻发生什么事情吗?如果是,您是否对调用此内容的线程有任何偏好?如果是,您是否对调用某物的计时器的准确性有任何偏好?例如,对的解决方案是什么?是不是太准确了?太不准确了?刚刚好?你查过连接池了吗?如果您使用的是标准框架类,那么它们会为您解决所有这些问题。计时器必须不准确。如果精确到几秒钟,就可以了。当某个物体已经5分钟没有使用时,它应该被放置在背景中。你预计在任何时候最多有多少个物体?另外,平均来说,您要多久调用一个对象上的方法才能重新启动其过期计时器?我理解这一点,但这不是我真正需要的:在您的建议中,我必须调用TryDoSomething来尝试对该对象执行某些操作,当该对象过期时,我会注意到这一点。但是,我想在一段时间没有使用对象的情况下完全删除该对象,因此该机制必须在后台自动运行。当然,轮询是一种选择,但我想避免它。@MichaelW-hmmm。我认为及时处理过期对象的要求是一个重要信息,应该包括在问题中。你能编辑这个问题并把它添加到那里吗?顺便说一句,请查看颇受欢迎的
MemoryCache
组件:“过期不会在后台发生。没有计时器主动扫描缓存中的过期项目。缓存上的任何活动(获取、设置、删除)都可以触发对过期项目的后台扫描。”
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
    }
}