У меня есть код, который выглядит следующим образом:
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);
}
Кажется, проблема в том, что отпечатки показывают следующее:
08:59:59,985: -15
09:00:00,015: -10
Итак, первая попытка действительно была за 15 миллисекунд до 9 утра. Однако второй попробовал через 15 миллисекунд после 9 утра вместо 10 миллисекунд до этого.
Как я могу гарантировать, что эти запросы будут отправлены именно в то время, когда я хочу?
Я использую Ubuntu*
Насколько точным вам это нужно? Сомневаюсь, что вы угадаете это с точностью до миллисекунды. Windows не является ОС реального времени, и dotnet, конечно же, не добавляет возможностей работы в реальном времени. Чтобы приблизиться к наилучшему результату, проще всего было бы использовать планировщик lib/ framework...
Таймеры Windows по умолчанию обычно имеют разрешение около 15 мс, поэтому смещение в 10 мс просто не сработает. Кроме того, то, что задержка закончилась, не означает, что ОС немедленно снова запустит поток вашего процесса. Если вам действительно нужно сделать это с помощью .net, увеличивающего приоритет вашего потока/процесса и вызывающего timeBeginPeriod(1), если вы работаете в Windows, это ваш лучший выбор. И даже тогда вы можете не достичь желаемой точности.
Какой источник времени использует ваш компьютер? Компьютерные часы реального времени обычно не настолько точны, чтобы их можно было синхронизировать, чтобы приблизиться к атомным часам. И, похоже, это то, что вам нужно, чтобы сделать запрос на другой сервер в точное время с точностью до миллисекунды.
Я использую Убунту. Какое планирование lib/framework. могу ли я использовать? И вы говорите, не используйте .NET. Что еще может быть более производительным? И поэтому понятия не имею, какой источник времени использует мой ноутбук. В любом случае идея заключалась бы в том, чтобы поставить его на EC2.
Не лучше ли было бы вместо этого распределить запрос по потокам?
Похоже, вы хотите сделать DDOS?
@NicolasREY, ты не ответил, насколько точным ты хочешь быть и почему. Это ОЧЕНЬ важно. Планировщики общего назначения любой ОС не гарантируют миллисекундную точность. Когда требуется такая точность, все ОС используют специализированные мультимедийные или планировщики реального времени. Вы можете уменьшить разрешение таймера системы, как это делал Chrome, но это приводит к высокому энергопотреблению, что было распространенной жалобой Chrome в прошлом. Увеличение разрешения таймера также не устраняет неизбежный перекос.
@NicolasREY в любом случае это не имеет ничего общего с производительностью, .NET или библиотеками планирования - если ОС не планирует ваш поток, он не запустится. EC2 будет пытаться перевести ядра в спящий режим даже более агрессивно, чем ваш ноутбук. Если вы используете планировщик ОС общего назначения для анимации, она будет заикаться. Ubuntu Pro имеет ядро реального времени, но требует подписки.





Похоже, проблема может быть связана с тем, как вы рассчитываете задержку для каждого запроса.
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;
}
Я вычисляю текущее время, прежде чем рассчитывать время ожидания. Проверьте, является ли рассчитанное время ожидания отрицательным, то есть время начала уже прошло. В таких случаях я не откладываю и приступаю немедленно. Используйте List<Task<Request>>, чтобы собрать задачи для каждого запроса, а затем дождитесь Task.WhenAll, чтобы дождаться завершения всех запросов.
выход:
08:59:59.985: -15
09:00:00.000: -10
Я думаю, что лучше всего подождать с помощью таймера (Task.Delay) до тех пор, пока не останется несколько миллисекунд до целевого времени, а затем подождать последние миллисекунды, вращая. В примере ниже продолжительность вращения установлена примерно на 20 миллисекунд:
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();
Вращение потребляет физический поток ЦП, поэтому вам следует избегать вращения большего количества потоков, чем количество физических ядер ЦП вашего компьютера. Более сложный подход состоял бы в том, чтобы выделить для вращения только один высокоприоритетный главный поток, который будет управлять другими рабочими потоками, сигнализируя о компонентах ManualResetEventSlim.
Как я могу гарантировать, что эти запросы отправляются именно в то время, когда я хочу: не используйте Windows и не используйте .NET или .NET Framework.