Спинвейт против ожидания сна. Какой из них использовать?
Является ли это эффективным для
SpinWait.SpinUntil(() => myPredicate(), 10000)
Для таймаута 10000ms
Или
Эффективнее ли использовать Thread.Sleep опрос для того же условия
Например, что-то вроде следующей функции SleepWait:
public bool SleepWait(int timeOut)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut)
{
Thread.Sleep(50)
}
return myPredicate()
}
Я обеспокоен тем, что вся отдача SpinWait может не быть хорошей моделью использования, если мы говорим о таймаутах в течение 1 секунды? Является ли это справедливым предположением?
Какой подход вы предпочитаете и почему? Есть ли другой еще лучше подходить?
Update - становится более конкретным:
Есть ли способ сделать блокирующий импульс сбора спящим потоком, когда он достигает ограниченной емкости? Я предпочитаю избегать напряженного ожидания всех вместе, как предлагает Марк Гравел.
2 ответов:
Лучшийподход состоит в том, чтобы иметь некоторый механизм для активного обнаружениятого, что становится истинным (а не пассивно опрашивать его, когда становитсяистинным); это может быть любой вид дескриптора ожидания, или, возможно,
TaskсWait, или, возможно,event, на который вы можете подписаться, чтобы отклеить себя. Конечно, если вы делаете что-то вроде "подождите, пока что-то не произойдет", это все равно не так эффективно, как просто выполнить следующий бит работы в качестве callback , что означает: вам не нужно использовать поток для ожидания.TaskимеетContinueWithдля этого, или вы можете просто сделать работу вevent, когда его уволят.event, вероятно, самый простой подход, в зависимости от контекста.Task, однако, уже предоставляет почти все, о чем вы здесь говорите, включая механизмы "ожидания с таймаутом" и "обратного вызова".И да, вращение в течение 10 секунд не велико. Если вы хотите использовать что-то вроде вашего текущего кода, и если у вас есть причина ожидать короткой задержки, но нужно учитывать более длинную-может быть,
SpinWaitдля (скажем) 20 мс, а использоватьSleepдля остальных?
Re комментарий; Вот как я бы зацепил механизм "is it full":
private readonly object syncLock = new object(); public bool WaitUntilFull(int timeout) { if(CollectionIsFull) return true; // I'm assuming we can call this safely lock(syncLock) { if(CollectionIsFull) return true; return Monitor.Wait(syncLock, timeout); } }С, в коде "положить обратно в коллекцию":
if(CollectionIsFull) { lock(syncLock) { if(CollectionIsFull) { // double-check with the lock Monitor.PulseAll(syncLock); } } }
В .NET 4
SpinWaitвыполняет интенсивное вращение процессора в течение 10 итераций, прежде чем дать результат. Но он не возвращается к вызывающему объекту сразу после каждого из этих циклов; вместо этого он вызываетThread.SpinWaitдля запуска через CLR (по сути, ОС) в течение заданного периода времени. Этот период времени первоначально составляет несколько десятков наносекунд, но удваивается с каждой итерацией, пока не завершатся 10 итераций. Это обеспечивает ясность / предсказуемость в общей затраченной фазе вращения (интенсивной для процессора), которая система может настраиваться в соответствии с условиями(количество ядер и т.д.). ЕслиSpinWaitостается в фазе спина слишком долго, он периодически будет спать, чтобы позволить другим потокам продолжить работу (см. блог Дж.Альбахари для получения дополнительной информации). Этот процесс гарантированно держит ядро занятым...Таким образом,
SpinWaitограничивает процессорный спин заданным числом итераций, после чего он дает свой временной срез на каждом спине (фактически вызываяThread.YieldиThread.Sleep), снижая свой ресурс потребление. Он также определит, работает ли пользователь на одной базовой машине, и даст результат на каждом цикле, если это так.С помощью
Thread.Sleepпоток блокируется. Но этот процесс не будет таким дорогим, как вышеописанный с точки зрения процессора.
Comments