【问题标题】:Replacing Thread.Sleep with Task.Delay用 Task.Delay 替换 Thread.Sleep
【发布时间】:2019-11-12 14:35:59
【问题描述】:

我正在重构使用Thread.Sleep 的代码,并且在出现错误时重试SQL 查询的时间限制越来越长。用于阻塞的Thread.Sleep 的常见替代品似乎是await Task.Delay,这需要将方法更改为async。该方法现在看起来像这样(为简洁起见,删除了额外的错误检查):

    private static async Task<int> PrepareForRetry( int retryCount, SqlCommand command, int timeout )
    {
        ++retryCount;
        if (retryCount < ConnectionRetryCount)
        {
            int SleepTime = _connectionRetryBackoffTimes[retryCount - 1] + (_connectionRetryRandomBackoff.Next() % 500);
            //Thread.Sleep(SleepTime);
            await Task.Delay(SleepTime);
        }
        return retryCount;
    }

我遇到的问题是async 要求调用方法为async,依此类推。尽管最终重构为完全异步,但这远远超出了当前的重构范围。

我遇到的问题是如何从同步代码中调用方法并获取结果。使用

retryCount = PrepareForRetry(retryCount, command, timeout).Result;

创建死锁,因为 UI 线程正在调用该方法。我发现我可以通过将Task.Delay 更改为

来解决此问题
await Task.Delay(SleepTime).ConfigureAwait(false);

但我不完全明白这是做什么的。我也尝试过使用

调用该方法
retryCount = Task.Run(async () => { await PrepareForRetry(retryCount, command, timeout).Result; });

但这有错误“'int'不包含'GetAwaiter'的定义”,我无法找出如何继续获得结果。使用 Task.Delay 是否是创建延迟的正确方法(计时器不允许增加等待时间),如果是,我应该如何调用该方法来获取返回值?

【问题讨论】:

  • 旁注:你为什么这样做?除非你全力以赴,让一切都正确异步,否则本地 UI 应用程序没有任何好处(请参阅stackoverflow.com/questions/20082221/…,根据你的实际目标,它甚至可能重复)。旁注 2:我将从顶部而不是底部开始转换 - 可以从 async 调用同步方法,但实际上不是相反。
  • 从顶部开始转换,而不是从底部开始 - +1
  • 经验法则是,如果您需要在 async 方法上调用 .Result,或者您要将其卸载到带有 Task.Run 的新 线程,那么您会更很可能做错了什么,简而言之,使用Thread.Sleep,除非你想将整个调用堆栈传播到async
  • 我不明白延迟的必要性。您是否正在尝试处理数据库连接的超时问题?有更好的方法来处理。
  • @JohnWu - 我很想以正确的方式处理这个问题。这是我们 DAL 的根,显然如果数据库访问失败,我们的程序将无法工作。我正在尝试找到处理 SqlException 的正确方法,以最大可能地检索或保存数据,但不知道从哪里开始。你能推荐一个文章或模式名称吗?

标签: c# asynchronous async-await task


【解决方案1】:

您应该使用与实际操作相同的样式来处理延迟。如果您的SqlCommand 是异步执行的,则使用Task.Delay 进行延迟。如果您的SqlCommand 是同步执行的,则使用Thread.Sleep 进行延迟。

听起来你仍然需要同步执行你的SqlCommand(至少现在是这样),所以你应该使用Thread.Sleep。最终,您可以使它们都异步(同时),但听起来那是另一天的工作。

Task.Delay 以某种方式替换Thread.Sleep,这是真的。只是Task.DelayThread.Sleep 的异步等效

【讨论】:

  • 感谢您的澄清。我们完成了代码审查,并被告知我们应该删除所有 Thread.Sleeps,所以我的任务是这样做。其中许多我可以转换为计时器,但这个似乎需要更深入的重构。
  • 在这里删除Thread.Sleep 理论上是好的,但它应该与使SqlCommand 异步一起完成,并将该异步传播到代码库的其他地方。我不会说“Thread.Sleep 很糟糕”作为一揽子声明;在这种情况下没关系(假设您还没有准备好进行这种异步重构)。如果您想遵守法律条文而不是精神,那么您可以使用Polly 进行重试逻辑,它在内部使用等效于Thread.Sleep 的同步重试。
猜你喜欢
  • 2014-10-07
  • 1970-01-01
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多