简体   繁体   English

使用Thread.Sleep()总是很糟糕吗?

[英]Is it always bad to use Thread.Sleep()?

I created an extension method for the the class Random which executes an Action (void delegate) at random times: 我为Random类创建了一个扩展方法,该方法在随机时间执行一个Action (无效委托):

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

It works fine. 工作正常。

But is the use of Thread.Sleep() in this example okay, or should you generally never use Thread.Sleep() , what complications could occur ? 不过是利用Thread.Sleep()在这个例子好了,还是应该你一般不会使用Thread.Sleep() ,会发生什么并发症? Are there alternatives? 有其他选择吗?

Is using Thread.Sleep bad? 使用Thread.Sleep是否不好? Generally not, if you really want to suspend the thread . 如果您确实想暂停线程 ,通常不会。 But in this case you don't want to suspend the thread , you want to suspend the task . 但是在这种情况下,您不想挂起线程 ,您想要挂起任务

So in this case, you should use: 因此,在这种情况下,您应该使用:

await Task.Delay(minDuration);

This will not suspend the entire thread, but just the single task you want to suspend. 这不会挂起整个线程,而只是挂起您要挂起的单个任务。 All other tasks on the same thread can continue running. 同一线程上的所有其他任务可以继续运行。

One reason I would use Task.Delay over Thread.Sleep is the fact that you can pass a CancellationToken to it. 我在Thread.Sleep上使用Task.Delay的原因之一是您可以将CancellationToken传递给它。 If the user wants to StopExecutor and the random received a long duration span, you'll end up blocking for a long time. 如果用户希望使用StopExecutor且随机StopExecutor接收到较长的持续时间,则您将长时间阻塞。 On the other hand, in Task.Delay , you can cancel the operation and it will be notified of that cancellation. 另一方面,在Task.Delay ,您可以取消操作,并且将通知该取消。

I think there are other problems with the design you choose to make. 我认为您选择制作的设计还有其他问题。 The Random class isn't really fit to be a task scheduler. Random类并不适合用作任务调度程序。 I would find it a bit odd to find a ExecuteRandomAsync , since it mostly doesn't execute a random, but execute some arbitrary Action every X minutes. 我发现找到ExecuteRandomAsync有点奇怪,因为它通常不执行随机操作,而是每X分钟执行一些任意的Action

I would instead, go about this another way. 我会改用另一种方式。 While keeping most of the internals you've already created, but putting them in a different class. 在保留您已经创建的大多数内部结构的同时,将它们放在不同的类中。

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

Sleep "talks" to the Operation System that you want to suspend the thread. Sleep与要挂起线程的操作系统“交谈”。 It's resource-intensive operation cause your thread uses RAM anyway (although it doesn't require processing time). 这是资源密集型操作,会导致您的线程仍在使用RAM(尽管它不需要处理时间)。

With thread pool, you could use thread's resources (fe RAM) to process some other small tasks. 使用线程池,您可以使用线程的资源(fe RAM)处理其他一些小任务。 To do it, the Windows lets you put a thread to sleep in a special alertable state, so it may be awakened and used temporarily. 为此,Windows允许您以特殊的可警告状态将线程置于睡眠状态,因此可以将其唤醒并暂时使用。

So Task.Delay let you put threads to alertable sleep, and therefore let you use resources of these thread unitl you don't need them. 因此Task.Delay允许您将线程置于可Task.Delay睡眠状态,因此可以让您使用不需要这些线程的资源。

Think of it this way. 这样想吧。

Sleeping a thread is dangerous because more often than not more than one tasks relies on that thread, not to mention key components of the program may do also. 休眠线程是危险的,因为不止一个任务要依赖该线程,更不用说程序的关键组件了。 Never using sleep on threads isn't appropriate, you will want to use them when they are beneficial to your program. 永远不要在线程上使用sleep是不合适的,当它们对程序有益时,您将希望使用它们。

Lots of people are taught not to sleep threads because of the unpredictable behaviour. 由于不可预测的行为,许多人被教导不要睡觉。 It's like managing a team, eventually you're going to have to let some go to lunch and as long as the rest are functioning whilst the ones you chose to go to lunch are out, then you should be fine and be able to work still. 这就像管理一个团队,最终您将不得不让一些人去吃午饭,而只要其他人正常工作,而您选择去吃午饭的人不在外面,那么您应该没事,仍然可以工作。

Just don't send people to lunch if there's people in the project reliant on their presence. 如果项目中有人依靠他们的存在,只是不要让人们共进午餐。

All answers are correct, and I would like to add a practical view: 所有答案都是正确的,我想添加一个实际的观点:

When you have a remote non real-time component to test (for example, a search engine), you need to give it time to go to it's new state before you reference it again for assertions. 当您有一个要测试的远程非实时组件(例如,搜索引擎)时,您需要给它时间进入新状态,然后再次引用它进行断言。 In other words, you want to suspend your test for some time. 换句话说,您想暂停测试一段时间。 Since the performance the test itself is irrelevant (please distinguish between the performance of the test and the performance of the component ), you sometimes prefer to keep your code (of the test) as simple and straight forward as possible, and you avoid even the minimum complexity of asynchronous code. 由于测试本身的性能无关紧要(请区分测试的性能和组件的性能),因此有时您希望保留(测试的) 代码尽可能简单明了,甚至避免异步代码的最小复杂度。 You could of course start a new test instead of waiting for your component (that's the difference between await Task.Delay() and Thread.Sleep() ), but the assumption was that you are in a no hurry for that. 您当然可以开始一个新的测试,而不用等待组件(这就是await Task.Delay()Thread.Sleep()之间的区别),但是假设您并不急于这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM