skip to Main Content

I have a code that looks like this:

public static async Task<Request> SendRequest(Client client, DateTime startTime, int offset)
{
    TimeSpan timeToWait = startTime - DateTime.UtcNow + TimeSpan.FromMilliseconds(offset);
    await Task.Delay(timeToWait);
    Console.Write(offset);
    Request request = client.sendRequest();
}

async static public Task<Request> SendMultiple(Client client, DateTime startTime)
{
    var offsets = new[] { -15, -10 };
    offsets.ForEach(offset => tasks.Add(SendRequest(client, startTime, offset));
    Request[] result = await Task.WhenAll(tasks);
}

The problem it seems, is that the prints show that:

08:59:59,985: -15
09:00:00,015: -10

So the first try was indeed, 15 milliseconds before 9AM. However, the second tried 15 milliseconds after 9AM instead of 10 milliseconds prior.

How could I ensure that those requests are being send exactly at the time I want, please?

I am using Ubuntu*

2

Answers


  1. Looks like the issue might be related on how you are calculating the time delay for each request.

    public static async Task<Request> SendRequest(Client client, DateTime startTime, int offset)
    {
        DateTime currentTime = DateTime.UtcNow;
        TimeSpan timeToWait = startTime - currentTime + TimeSpan.FromMilliseconds(offset);
        
        if (timeToWait.TotalMilliseconds > 0)
            await Task.Delay(timeToWait);
        
        Console.WriteLine($"{DateTime.UtcNow.ToString("hh:mm:ss.fff")}: {offset}");
        Request request = client.sendRequest();
        return request;
    }
    
    async static public Task<Request[]> SendMultiple(Client client, DateTime startTime)
    {
        var offsets = new[] { -15, -10 };
        List<Task<Request>> tasks = new List<Task<Request>>();
        
        foreach (var offset in offsets)
        {
            tasks.Add(SendRequest(client, startTime, offset));
        }
        
        Request[] result = await Task.WhenAll(tasks);
        return result;
    }
    

    I calculate the current time before calculating the time to wait. Check if the calculated time to wait is negative, meaning that the start time has already passed. In such cases, I don’t delay and proceed immediately. Using a List<Task<Request>> to collect the tasks for each request and then await Task.WhenAll to wait for all requests to complete.

    output:

    08:59:59.985: -15
    09:00:00.000: -10
    
    Login or Signup to reply.
  2. I think that your best bet is to wait with a timer (Task.Delay) until a few milliseconds before the target time, and then wait the final milliseconds by spinning. In the example below the spinning duration is set to about 20 milliseconds:

    DateTime targetTime = startTime + TimeSpan.FromMilliseconds(offset);
    TimeSpan timeToWait = targetTime - DateTime.UtcNow - TimeSpan.FromMilliseconds(20);
    
    if (timeToWait > TimeSpan.Zero) await Task.Delay(timeToWait); // Wait with timer
    
    while (DateTime.UtcNow < targetTime); // Spin-wait
    
    Request request = client.sendRequest();
    

    Spinning consumes a physical CPU thread, so you should avoid spinning on more threads than the number of physical CPU cores of your machine. A more sophisticated approach would be to dedicate just one thread for spinning, which would control other worker threads by signaling ManualResetEventSlim components.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search