How to send multiple requests to a server at few milliseconds of interval in C#

87 Views Asked by At

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

There are 2 best solutions below

0
Theodor Zoulias On BEST ANSWER

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 high-priority master thread for spinning, which would control other worker threads by signaling ManualResetEventSlim components.

0
Sandeep Roy On

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