Должны ли взаимосвязанные реализации на основе CompareExchange использовать SpinWait?
Ниже представлена реализация метода блокировки на основе Interlocked.CompareExchange
.
Целесообразно ли для этого кода использовать SpinWait
вращать перед повторением?
public static bool AddIfLessThan(ref int location, int value, int comparison)
{
int currentValue;
do
{
currentValue = location; // Read the current value
if (currentValue >= comparison) return false; // If "less than comparison" is NOT satisfied, return false
}
// Set to currentValue+value, iff still on currentValue; reiterate if not assigned
while (Interlocked.CompareExchange(ref location, currentValue + value, currentValue) != currentValue);
return true; // Assigned, so return true
}
я видел SpinWait
используется в этом сценарии, но моя теория состоит в том, что в этом нет необходимости. В конце концов, цикл содержит только несколько инструкций, и всегда есть один поток, выполняющий работу.
Предположим, что два потока спешат выполнить этот метод, и первый поток сразу же преуспевает, тогда как второй поток изначально не вносит изменений и должен повторить. При отсутствии других претендентов, может ли второй поток вообще потерпеть неудачу при второй попытке?
Если второй поток примера не может дать сбой при второй попытке, то что мы можем получить с помощью SpinWait
? Избавиться от нескольких циклов в том маловероятном случае, когда сотни потоков спешат выполнить этот метод?
2 ответа
Мое неспециализированное мнение заключается в том, что в этом конкретном случае, когда два потока иногда вызывают AddIfLessThan
, а SpinWait
не нужно. Это могло быть полезно, если бы оба потока вызывалиAddIfLessThan
в плотном цикле, чтобы каждый поток мог непрерывно работать в течение нескольких секунд.
На самом деле я провел эксперимент и измерил производительность одного вызова потока AddIfLessThan
в тугой петле против двух потоков. Двум нитям нужно почти в четыре раза больше, чтобы сделать одинаковое количество петель (в совокупности). ДобавлениеSpinWait
смешивание делает два потока лишь немного медленнее, чем одиночный поток.
Две нити просто не предмет для SpinWait
обсуждение. Но этот код не сообщает нам, сколько потоков фактически могут конкурировать за ресурс, и с относительно большим количеством потоков, использующихSpinWait
может стать полезным. В частности, с большим количеством потоков виртуальная очередь потоков, которые пытаются успешно получить ресурс, становится длиннее, и те потоки, которые в конечном итоге обслуживаются, имеют хорошие шансы превысить свой временной интервал, выделенный планировщиком, который, в свою очередь, может приводят к более высокому потреблению ЦП и могут повлиять на выполнение других запланированных потоков даже с более высоким приоритетом. ВSpinWait
имеет хороший ответ на эту ситуацию, устанавливая верхний предел разрешенных спинов, после которого будет выполнено переключение контекста. Таким образом, это разумный компромисс между необходимостью сделать дорогостоящий системный вызов, чтобы вызвать переключение контекста, и неконтролируемым потреблением ЦП в пользовательском режиме, которое может повлиять на выполнение других потоков в определенных ситуациях.