C# 使用Thread.Sleep()总是不好的吗?

C# 使用Thread.Sleep()总是不好的吗?,c#,multithreading,sleep,C#,Multithreading,Sleep,我为类Random创建了一个扩展方法,它在随机时间执行操作(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,

我为类
Random
创建了一个扩展方法,它在随机时间执行
操作(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()
,会发生什么并发症?有其他选择吗?

正在使用
线程。睡眠不好?如果确实要挂起线程,通常不会。但在这种情况下,您不希望挂起线程,而是希望挂起任务

因此,在这种情况下,您应该使用:

await Task.Delay(minDuration);
这不会挂起整个线程,而只是挂起要挂起的单个任务。同一线程上的所有其他任务都可以继续运行。

这样想

休眠一个线程是危险的,因为通常不止一个任务依赖于该线程,更不用说程序的关键组件也可能依赖该线程。 从不在线程上使用
sleep
是不合适的,当它们对程序有益时,您会希望使用它们

很多人被教导不要睡觉,因为他们的行为不可预测。这就像管理一个团队,最终你将不得不让一些人去吃午饭,只要剩下的人在你选择去吃午饭的人外出时仍在工作,那么你就应该没事,还能继续工作

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

睡眠
与操作系统“对话”要挂起线程。这是一种资源密集型操作,因为线程无论如何都会使用RAM(尽管它不需要处理时间)

使用线程池,您可以使用线程的资源(即RAM)来处理其他一些小任务。要做到这一点,Windows允许您将一个线程置于一种特殊的可报警状态下,使其进入睡眠状态,因此它可能会被唤醒并暂时使用


因此,
Task.Delay
允许您将线程置于可警报的睡眠状态,因此允许您使用这些线程单元的资源。您不需要它们。

我之所以使用
Task.Delay
覆盖
thread.sleep
是因为您可以将
取消令牌
传递给它。如果用户想
停止Executor
,而随机数据接收到的持续时间跨度很长,那么最终会阻塞很长时间。另一方面,在
Task.Delay
中,您可以取消该操作,并将收到取消通知

我认为你选择的设计还有其他问题。
Random
类实际上不适合作为任务调度器。我会觉得找到一个
ExecuteRandomAsync
有点奇怪,因为它通常不会执行随机操作,而是每隔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();
        }
    }
}

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


当您有一个远程非实时组件要测试(例如,搜索引擎)时,您需要在再次引用它进行断言之前,给它时间进入新状态。换句话说,您希望暂停测试一段时间。由于测试本身的性能是不相关的(请区分测试的性能和组件的性能),因此您有时更喜欢将代码保持尽可能简单和直接,您甚至可以避免异步代码的最小复杂性。当然,您可以启动一个新的测试,而不是等待组件(这就是
等待Task.Delay()
线程.Sleep()
)之间的区别,但是假设您并不急于进行测试。

我不明白区别是什么,任务不只是一个托管线程吗?Thread.Sleep()也只阻塞当前任务,否则我将无法工作。不,它们不一样。如果你不等待
任务。延迟
,你根本就没有做任何事情。简单解释@NightSkyCode为什么你不能等待?好吧,这是一个有益的例子,还是我应该使用其他方法?@Patrickhoffman已经深入研究了整个线程睡眠的替代方案。。。虽然除了使用其他方法(线程外或逻辑内)解决问题外,没有其他真正的方法可以替代真正的线程睡眠。在哪里实现持续时间?@Berntonline您不需要睡眠两次。