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

Всегда ли использовать Thread.Sleep()?

Я создал метод расширения для класса Random, который в случайное время выполняет Action (void delegate):

public static class RandomExtension
{
    private static bool _isAlive;
    private static Task _executer;

    public static void ExecuteRandomAsync(this Random random, int min, int max, int minDuration, Action action)
    {
        Task outerTask = Task.Factory.StartNew(() =>
        {
            _isAlive = true;
            _executer = Task.Factory.StartNew(() => { ExecuteRandom(min, max, action); });
            Thread.Sleep(minDuration);
            StopExecuter();
        });
    }

    private static void StopExecuter()
    {
        _isAlive = false;
        _executer.Wait();

        _executer.Dispose();
        _executer = null;
    }

    private static void ExecuteRandom(int min, int max, Action action)
    {
        Random random = new Random();

        while (_isAlive)
        {
            Thread.Sleep(random.Next(min, max));
            action();
        }
    }
}

Он отлично работает.

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

4b9b3361

Ответ 1

Использует Thread.Sleep плохо? Как правило, нет, если вы действительно хотите приостановить поток. Но в этом случае вы не хотите приостанавливать поток, вы хотите приостановить выполнение задачи.

Итак, в этом случае вы должны использовать:

await Task.Delay(minDuration);

Это не приведет к приостановке всего потока, а только одной задачи, которую вы хотите приостановить. Все остальные задачи в одном потоке могут продолжаться.

Ответ 2

Sleep "разговаривает" с операционной системой, что вы хотите приостановить поток. Это ресурсоемкая операция заставляет ваш поток использовать RAM в любом случае (хотя это не требует времени обработки).

С пулом потоков вы можете использовать ресурсы потоков (например, ОЗУ) для обработки некоторых других небольших задач. Для этого Windows позволяет помещать поток в sleep в специальном предупреждающем состоянии, поэтому его можно разбудить и использовать временно.

Итак Task.Delay позволяет помещать потоки в аварийный сон и, следовательно, позволить вам использовать ресурсы этих потоков, которые вам не нужны.

Ответ 3

Одной из причин, по которой я бы использовал Task.Delay over Thread.Sleep, является то, что вы можете передать ему CancellationToken. Если пользователь хочет StopExecutor, а случайный получал длительный промежуток времени, вы в конечном итоге блокируете в течение длительного времени. С другой стороны, в Task.Delay вы можете отменить операцию, и она будет уведомлена об этом отмене.

Я думаю, что есть другие проблемы с дизайном, который вы выберете. Класс Random не подходит для планировщика задач. Мне было бы немного странно найти ExecuteRandomAsync, поскольку он в большинстве случаев не выполняет случайный, но выполняет произвольные Action каждые X минут.

Вместо этого я пошел бы по этому пути. Сохраняя большинство внутренних компонентов, которые вы уже создали, но помещаете их в другой класс.

public class ActionInvoker
{
    private readonly Action _actionToInvoke;

    public ActionInvoker(Action actionToInvoke)
    {
        _actionToInvoke = actionToInvoke;
        _cancellationTokenSource = new CancellationTokenSource();
    }

    private readonly CancellationTokenSource _cancellationTokenSource;
    private Task _executer;

    public void Start(int min, int max, int minDuration)
    {
        if (_executer != null)
        {
            return;
        }

        _executer = Task.Factory.StartNew(
                    async () => await ExecuteRandomAsync(min, max, _actionToInvoke),
                    _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, 
                    TaskScheduler.Default)
                    .Unwrap();
    }

    private void Stop()
    {
        try
        {
            _cancellationTokenSource.Cancel();
        }
        catch (OperationCanceledException e)
        {
            // Log the cancellation.
        }
    }

    private async Task ExecuteRandomAsync(int min, int max, Action action)
    {
        Random random = new Random();

        while (!_cancellationTokenSource.IsCancellationRequested)
        {
            await Task.Delay(random.Next(min, max), _cancellationTokenSource.Token);
            action();
        }
    }
}

Ответ 4

Подумайте об этом таким образом.

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

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

Просто не отправляйте людей на обед, если люди в проекте полагаются на их присутствие.

Ответ 5

Все ответы верны, и я хотел бы добавить практический вид:

Если у вас есть удаленный компонент, не являющийся в реальном времени, для тестирования (например, поисковая система), вам нужно дать ему время, чтобы перейти к новому состоянию, прежде чем ссылаться на него снова для утверждений. Другими словами, вы хотите приостановить тест на некоторое время. Поскольку производительность сама по себе не имеет значения (пожалуйста, различайте производительность test и производительности компонента), вы иногда предпочитаете хранить код (теста) как можно более простым и прямым, и вы избегаете даже минимальной сложности асинхронного кода. Разумеется, вы можете начать новый тест вместо того, чтобы ждать своего компонента (разница между await Task.Delay() и Thread.Sleep()), но предположение состояло в том, что вы не торопитесь для этого.