使用Thread.Sleep()总是坏的吗?

时间:2015-08-07 08:32:06

标签: c# multithreading sleep

我为类Random创建了一个扩展方法,该方法随机执行Action(void委托):

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(),可能会出现什么并发症?还有其他选择吗?

5 个答案:

答案 0 :(得分:44)

使用Thread.Sleep不好吗?通常不会,如果你真的想暂停线程。但在这种情况下,您不想暂停线程,您想要暂停任务

所以在这种情况下,你应该使用:

await Task.Delay(minDuration);

这不会暂停整个线程,只会暂停您要暂停的单个任务。同一线程上的所有其他任务都可以继续运行。

答案 1 :(得分:8)

我使用Task.Delay优于Thread.Sleep的一个原因是您可以将CancellationToken传递给它。如果用户想要StopExecutor并且随机收到的持续时间很长,那么您最终会阻塞很长时间。另一方面,在Task.Delay中,您可以取消该操作,并将通知该取消。

我认为您选择的设计存在其他问题。 Random类并非真正适合作为任务调度程序。我发现找ExecuteRandomAsync有点奇怪,因为它主要不执行随机,但每X分钟执行一次任意Action

相反,我会采取另一种方式。保留你已经创建的大部分内部结构,但将它们放在不同的类中。

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

答案 2 :(得分:7)

Sleep"会谈"到要挂起线程的操作系统。它的资源密集型操作会导致您的线程使用RAM(尽管它不需要处理时间)。

使用线程池,您可以使用线程的资源(f.e.RAM)来处理其他一些小任务。为此,Windows允许您将线程置于睡眠处于特殊警报状态,因此可能会被唤醒并暂时使用。

所以Task.Delay让你把线程放到可警告的睡眠状态,因此你可以使用你不需要它们的这些线程单元的资源。

答案 3 :(得分:4)

这样想。

睡眠线程是危险的,因为通常不止一个任务依赖于该线程,更不用说程序的关键组件也可能。 从不在线程上使用sleep是不合适的,如果它们对您的程序有益,您将需要使用它们。

由于不可预测的行为,很多人被教导不要睡觉。这就像管理团队一样,最终你不得不让一些人去吃午餐,只要剩下的都在运行,而你选择去吃午餐的人都出去了,那么你应该没事,能够继续工作

如果项目中的人依赖他们的存在,就不要让人们去吃午饭。

答案 4 :(得分:0)

所有答案都是正确的,我想补充一个实用的观点:

当你有一个要测试的远程非实时组件(例如,一个搜索引擎)时,你需要给它时间去它的新状态再次引用断言之前。换句话说,您希望暂停测试一段时间。由于测试本身的性能无关紧要(请区分测试的性能与组件的性能),有时您更喜欢保留代码< / strong>(测试)尽可能简单直接,甚至避免了异步代码的最低复杂性。你当然可以开始一个新的测试,而不是等待你的组件(这是await Task.Delay()Thread.Sleep()之间的差异),但假设你并不着急。