Подтвердить что ты не робот

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

Эффективно ли это для

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

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

или

Насколько эффективнее использовать опрос 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 секунды? Действительно ли это предположение?

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


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

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

4b9b3361

Ответ 1

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

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


Повторите комментарий; здесь, как я зацепил механизм "это полный":

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

Ответ 2

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

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

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