It is unusual to demand a limit on the interval between certain events and take action if the limit is exceeded. For example, sending a heartbeat between network peers to detect that the other end is alive.
In C # async / await style, you can implement this by replacing the timeout task every time a heart rate arrives:
var client = new TcpClient { ... }; await client.ConnectAsync(...); Task heartbeatLost = new Task.Delay(HEARTBEAT_LOST_THRESHOLD); while (...) { Task<int> readTask = client.ReadAsync(buffer, 0, buffer.Length); Task first = await Task.WhenAny(heartbeatLost, readTask); if (first == readTask) { if (ProcessData(buffer, 0, readTask.Result).HeartbeatFound) { heartbeatLost = new Task.Delay(HEARTBEAT_LOST_THRESHOLD); } } else if (first == heartbeatLost) { TellUserPeerIsDown(); break; } }
This is convenient, but each instance of the Task delay has a Timer , and if many heartbeat packets arrive in less time than the threshold value, then many Timer objects load the threadpool. In addition, the completion of each Timer will run the code in threadpool, is there any continuation associated with it or not.
You cannot release the old Timer by calling heartbeatLost.Dispose() ; which will give an exception
InvalidOperationException : A task can only be deleted if it is in a completion state.
You can create a CancellationTokenSource and use it to cancel the old delay task, but it seems suboptimal to create even more objects to accomplish this when the timers themselves have a recalculation function.
What is the best way to integrate time redistribution so that the code can be structured more like this?
var client = new TcpClient { ... }; await client.ConnectAsync(...); var idleTimeout = new TaskDelayedCompletionSource(HEARTBEAT_LOST_THRESHOLD); Task heartbeatLost = idleTimeout.Task; while (...) { Task<int> readTask = client.ReadAsync(buffer, 0, buffer.Length); Task first = await Task.WhenAny(heartbeatLost, readTask); if (first == readTask) { if (ProcessData(buffer, 0, readTask.Result).HeartbeatFound) { idleTimeout.ResetDelay(HEARTBEAT_LOST_THRESHOLD); } } else if (first == heartbeatLost) { TellUserPeerIsDown(); break; } }