Как эффективно выполнять 50 параллельных методов 500 раз в секунду?
Мне нужно выполнить strategy.AllTablesUpdated();
для 50 стратегий за 2 мс (и мне нужно повторить это ~500 раз в секунду). Используя код ниже, я обнаружил, что просто Monitor.TryEnter
звоните тратится до 1 мс (!!!) и я делаю это 50 раз!
// must be called ~500 times per second
public void FinishUpdatingTables()
{
foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms
{
// this slow and can be paralleled
strategy.AllTablesUpdated();
}
}
...................
public override bool AllTablesUpdated(Stopwatch sw)
{
this.sw = sw;
Checkpoint(this + " TryEnter attempt ");
if (Monitor.TryEnter(desiredOrdersBuy))
{
Checkpoint(this + " TryEnter success ");
try
{
OnAllTablesUpdated();
} finally
{
Monitor.Exit(desiredOrdersBuy);
}
return true;
} else
{
Checkpoint(this + " TryEnter failed ");
}
return false;
}
public void Checkpoint(string message)
{
if (sw == null)
{
return;
}
long time = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
Log.Push(LogItemType.Debug, message + time);
}
Из журналов (в мкс) неудачная попытка потратила ~ 1 мс:
12: 55: 43: 778 Отладка: попытка TryEnter 1264 12:55:43:779 Отладка: ошибка TryEnter 2123
Из журналов (в мкс) успешная попытка затрачивается ~ 0,01 мс:
12: 55: 49: 701 Отладка: попытка TryEnter 889 12:55:49:701 Отладка: попытка TryEnter 900
Так что теперь я думаю, что Monitor.TryEnter
это слишком дорого для меня, чтобы быть выполненным один за другим для 50 стратегий. Так что я хочу параллельно эту работу, используя Task
как это:
// must be called ~500 times per second
public void FinishUpdatingTables()
{
foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms
{
// this slow and can be paralleled
Task.Factory.StartNew(() => {
strategy.AllTablesUpdated();
});
}
}
Я тоже наверное заменю Monitor.TryEnter
чтобы просто lock
как при таком подходе все будет асинхронно.
Мои вопросы:
- Зачем
Monitor.TryEnter
так медленно? (1 мс, если блокировка не получена) - Как хорошо было бы начать 50
Task
каждые 2 мс = 25 000 задач каждую секунду? Может ли.NET эффективно управлять этим? Я также могу использовать шаблон производитель-потребитель сBlockingCollection
и начать 50 "рабочих" только ОДИН РАЗ, а затем отправлять новый пакет из 50 предметов каждые 2 мс в BlockingCollection? Это было бы лучше? - Как бы вы выполнили 50 методов, которые можно выполнять параллельно каждые 2 мс (500 раз в секунду), всего 25 000 раз в секунду?
1 ответ
- Monitor.TryEnter(объект) - это просто Monitor.TryEnter(объект, 0, ref false) (время ожидания 0 миллисекунд). То, что 1 мс, если блокировка не получена, это просто накладные расходы на попытку получить блокировку.
- Вы можете запускать столько задач, сколько захотите, все они используют ThreadPool, хотя будет ограничено максимальным количеством потоков. Максимум зависит от вашей системы, количества ядер, памяти и т. Д. Это не будет 25 000 потоков, это точно. Однако, если вы начнете вмешиваться в планировщик TPL, у вас возникнут проблемы. Я бы просто использовал
Parallel.Foreach
и посмотрим, как далеко я это доберусь. Parallel.ForEach
, Я также гарантирую, чтоstrategies
имеет типIList
так что многие элементы запускаются, насколько это возможно, без ожидания итератора.
Вы не вставили код в OnAllTablesUpdated(), вы сохраняете блокировку на время этой процедуры. Это будет вашим узким местом во всех отношениях.
Некоторые вопросы, почему вы используете блокировку, когда таблица готова к обработке?
- Делегаты не возможны?
- Зачем блокировать его, когда вы используете стратегию? Вы модифицируете эту таблицу внутри каждой стратегии? Вы не можете взять его копию, если это так?