Будет ли зацикливание Thread.Sleep () плохим для производительности, когда используется для приостановки потока?

Существует (или было) много разговоров о том, хорошо или плохо использоватьThread.Sleep() метод. Из того, что я понимаю, это в основном для отладки.

Теперь я задаюсь вопросом: это плохо использовать для моей конкретной цели, то есть постоянно зацикливать его, чтобы иметь возможность приостановить / возобновить поток? Я делаю это потому, что хочу приостановить поток, который выполняет операции ввода-вывода, и иметь возможность возобновить его простым способом.

Операции ввода / вывода - это просто запись блоков размером 4096 байт в файл до тех пор, пока в него не будут записаны все данные. Поскольку файл может быть большим и занимать много времени, я хочу иметь возможность приостановить операцию (в случае, если он начнет потреблять много системных ресурсов).

Мой код, версия VB.NET:

'Class level.
Private BytesWritten As Long = 0
Private Pause As Boolean = False

'Method (thread) level.
While BytesWritten < [target file size]
    ...write 4096 byte buffer to file...

    While Pause = True
        Thread.Sleep(250)
    End While

    ...do some more stuff...
End While

C # эквивалент:

//Class level.
long bytesWritten = 0;
bool pause = false;

//Method (thread) level.
while(bytesWritten < [target file size]) {
    ...write 4096 byte buffer to file...

    while(pause == true) {
        Thread.Sleep(250);
    }

    ...do some more stuff...
}

Я слышал о ResetEvents и немного знаю о том, что они делают, но я никогда особо не разбирался в них.

 Raymond Chen23 июл. 2016 г., 22:45
Я бы использовал событие ручного сброса. После каждого куска ждите события. Чтобы приостановить поток, сбросьте событие. Чтобы возобновить поток, установите событие.
 Visual Vincent23 июл. 2016 г., 22:56
@EricLippert: Это если пользователь моего приложения хочет приостановить его, потому что операция может потреблять много системных ресурсов.
 Eric Lippert23 июл. 2016 г., 22:53
Я не понимаю, почему вы хотели быПауза a длительная эксплуатация, Конечно, это займет больше времени. Если вы хотите, чтобы файл записывался асинхронно, то почему бы просто не использовать асинхронный ввод-вывод файла и дождаться результата?

Ответы на вопрос(4)

я неправильно понимаю, чего вы пытаетесь достичь здесь, но из того, что я вижу, кажется, что вы пытаетесь заблокировать поток, пока не завершится задача ввода-вывода. Семафоры будут лучшим выбором здесь, вместо того, чтобы использовать Thread.Sleep ().

Большинство операционных систем предлагают блокирующие семафоры, которые переводят поток в спящий режим до тех пор, пока другой поток его не разбудит. , вам лучше использовать их, чем постоянно проверять себя.

И Thread.Sleep (), и блокирующие семафоры переводят поток в спящий режим, но последний делает это постоянно, пока ресурс не будет освобожден (семафор был подписан). Первый требует, чтобы поток постоянно просыпался, проверял и снова ложился спать. Последний сохраняет эти циклы выполнения.

 Visual Vincent23 июл. 2016 г., 23:32
Мне все еще нравится, что вы очень много отвечаете, но вместо этого я отметил ответ dbasnett как принятый, так как он лучше подходит для моего использования.ManualResetEvent позволяет / блокирует только один вызов, в то время как при использованииSemaphore Вы должны указатьКак много это должно позволить, пока это не блокирует.
 Visual Vincent23 июл. 2016 г., 23:06
Отличный ответ! Короткие и понятные объяснения!
 Visual Vincent23 июл. 2016 г., 22:33
Извините за то, что неясно. Я не пытаюсь заблокировать, пока операция ввода / вывода не закончится, я пытаюсь приостановить операцию ввода / вывода. Я в основном записываю буферы данных в файл до тех пор, пока все данные не будут записаны, так как это может занять некоторое время, чтобы я смог приостановить эту работу.
 Visual Vincent23 июл. 2016 г., 22:42
Я также обновил свой вопрос с этим объяснением.
Решение Вопроса

Я думаю, основываясь на описании, я бы сделал это

'Class level.
Private BytesWritten As Long = 0
Private NotPaused As New Threading.ManualResetEvent(True)

Изменение имени переменной подходит, так как это будет использоваться

    'Method (thread) level.
     While BytesWritten < [target file size]
        '...write 4096 byte buffer to file...

        NotPaused.WaitOne(-1)

        '...do some more stuff...
    End While

Чтобы сделать паузу цикла, сделайте это

    NotPaused.Reset()

и продолжить

    NotPaused.Set()
 Visual Vincent23 июл. 2016 г., 23:29
Спасибо. Это решение подходит лучше, так какManualResetEvent является только «открытым» или «закрытым», кромеSemaphore который, кажется, открыт дляx количество звонков. Теперь вы также научили меня, какManualResetEvents Работа!

В .NET нет причин использовать Thread.Sleep, кроме попытки имитировать длительные операции во время тестирования и / или отладки в потоке MTA, поскольку он будет блокироваться.

Возможно, другой вариант будет использоватьTPL, Так как вы не хотите блокировать, вы можете использоватьTask.Delay, Как вы, вероятно, знаете, Задача представляет собой асинхронную операцию.

что более элегантный способ - заставить поток спать бесконечно, пока он не проснется другим потоком, вызывающим Thread.Interrupt в первом потоке, который спит. Вот хороший пример этого с примером кода:Приостановка и возобновление потоков.

 Visual Vincent23 июл. 2016 г., 22:48
Спасибо за ответ. Но я, как и Гроо, знал об этомThread.Interrupt() может вызвать проблемы.
 Timo Salomäki23 июл. 2016 г., 22:53
Спасибо за исправление @Groo, я должен был также подумать о лучших практиках (что можно и чего нельзя делать) при ответе на вопрос.
 Groo23 июл. 2016 г., 22:46
Прерывание потоков - плохая идея, которая может легко привести к тупикам.

Ваш ответ на вопрос