SpinWait против ожидания сна. Какой из них использовать?


эффективно ли

SpinWait.SpinUntil(() => myPredicate(), 10000)

для тайм-аута 10000ms

или

это более эффективно использовать Thread.Sleep опрос для того же условия Например что-то вроде следующего :

public bool SleepWait(int timeOut)
{
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start();
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut)
    {
       Thread.Sleep(50)
    }  
    return myPredicate()
}

Я обеспокоен тем, что вся отдача SpinWait не может быть хорошей моделью использования, если мы говорим о таймаутах в течение 1 сек? Это правильное предположение?

какой подход вы предпочитаете и зачем? Есть ли другой, еще лучший подход?


обновление - становясь более конкретным:

есть ли способ сделать импульс BlockingCollection спящим потоком, когда он достигает ограниченной емкости? Я предпочитаю избегать занятого ожидания все вместе, как предлагает Марк гравий.

2 59

2 ответа:

The лучшие подход должен иметь какой-то механизм активно обнаруживать вещь становится правдой (вместо того, чтобы пассивно опрашивать ее, имея стать true); это может быть любой вид wait-handle, или, может быть,Task С Wait, или event что вы можете подписаться на отклеить себя. Конечно, если вы делаете что-то вроде "подождите, пока что-то не произойдет", то есть еще не так эффективно, как просто иметь следующий бит проделанной работы как обратный вызов, что означает: вам не нужно использовать нить, чтобы ждать. Task и ContinueWith для этого, или вы можете просто сделать работу в event когда он будет уволен. Элемент event - Это, наверное, самый простой подход, в зависимости от контекста. Task, однако, уже предоставляет большинство-все, о чем вы говорите здесь, включая механизмы "ожидания с таймаутом" и "обратного вызова".

и да, вращение в течение 10 секунд не очень хорошо. Если вы хотите использовать что - то вроде вашего текущего кода, и если у вас есть основания ожидать небольшую задержку, но нужно разрешить более длинный-возможно SpinWait на (скажем) 20 мс, и использовать Sleep для остальных?


Re комментарий; Вот как я бы крючок" это полный " механизм:

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);
    }
}

С, в коде" put back into the collection":

if(CollectionIsFull) {
    lock(syncLock) {
        if(CollectionIsFull) { // double-check with the lock
            Monitor.PulseAll(syncLock);
        }
    }
}

в .NET 4 SpinWait выполняет CPU-интенсивное вращение в течение 10 итераций перед выходом. Но он не возвращается к вызывающему тут после каждого из этих циклов; вместо этого, он называет Thread.SpinWait для вращения через CLR (по существу, ОС) в течение заданного периода времени. Этот период времени первоначально составляет несколько десятков нано-секунд, но удваивается с каждой итерацией до завершения 10 итераций. Это обеспечивает ясность / предсказуемость в общей фазе времени, затраченного на вращение (интенсивный процессор) , который система может настроить в соответствии с условиями (количество ядер и т. д.). Если SpinWait по-прежнему в спин-урожайный участок слишком долго он будет периодически спать, чтобы позволить другим потокам (см. Albahari J. В блог для получения дополнительной информации). Этот процесс гарантированно держать ядро занят...

и SpinWait ограничивает интенсивное вращение процессора заданным числом итераций, после чего он дает свой временной срез на каждом вращении (фактически вызывая Thread.Yield и Thread.Sleep), снизив ее потребление ресурсов. Он также определит, работает ли пользователь на одноядерной машине и выходит на каждый цикл, если это так.

С Thread.Sleep поток блокируется. Но этот процесс не будет таким дорогим, как выше с точки зрения процессора.