使用Task.Delay()需要花费什么?

What it costs to use Task.Delay()?
2021-07-22
  •  译文(汉语)
  •  原文(英语)

我正在考虑在具有事件驱动逻辑的MMO游戏服务器中使用C#async \ await.假设有成千上万的实体在进行一些持续时间已知的工作.所以我想Time.Delay()为我的每个游戏对象调用.(这是一种常见的无限循环的对立方法,Update()需要对每个游戏对象进行一些调用.)

有人知道如何Task.Delay()实现吗?使用计时器吗?它占用系统资源吗?

可以产生数千个同时Task.Delay()调用吗?

速聊1:
因为Thread.Sleep不是async吗?
速聊2:
而Task.Delay是吗?那没有道理.如果我想延迟一个线程,我现在就想要它,而不是生成另一个线程告诉一个线程停止.
速聊3:
关键是要让线程在恢复之前等待一段时间.关键不是要延迟线程.
速聊4:
等待/异步内容使调用Task.Delay()似乎立即从调用它的代码中返回,并且延迟之后的代码将在延迟结束后恢复.它是非阻塞的(只要调用它的方法本身就是async).
速聊5:
'sNotALie.因为Thread.Sleep使用0或1以外的参数进行调用始终是一个很大的设计问题.
解决过程1

Task.Delay 实现如下:

public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
{
  //error checking
  Task.DelayPromise delayPromise = new Task.DelayPromise(cancellationToken);
  if (cancellationToken.CanBeCanceled)
    delayPromise.Registration = cancellationToken.InternalRegisterWithoutEC((Action<object>) (state => ((Task.DelayPromise) state).Complete()), (object) delayPromise);
  if (millisecondsDelay != -1)
  {
    delayPromise.Timer = new Timer((TimerCallback) (state => ((Task.DelayPromise) state).Complete()), (object) delayPromise, millisecondsDelay, -1);
    delayPromise.Timer.KeepRootedWhileScheduled();
  }
  return (Task) delayPromise;
}

它绝对使用计时器.它们用于名为的类中DelayPromise.这是该代码的实现:

private sealed class DelayPromise : Task<VoidTaskResult>
{
  internal readonly CancellationToken Token;
  internal CancellationTokenRegistration Registration;
  internal Timer Timer;

  internal DelayPromise(CancellationToken token)
  {
    this.Token = token;
  }

  internal void Complete()
  {
    if (!(this.Token.IsCancellationRequested ? this.TrySetCanceled(this.Token) : this.TrySetResult(new VoidTaskResult())))
      return;
    if (this.Timer != null)
      this.Timer.Dispose();
    this.Registration.Dispose();
  }
}

它确实使用了计时器,但对我来说似乎并不担心.计时器只是回调到complete方法,然后执行的操作是检查是否取消了它,如果取消了,则返回一个结果.对我来说似乎很好.

速聊1:
我真的很惊讶它为每个Delay创建一个新的计时器对象.我本来以为它将与一个计时器一起使用某些增量队列.
速聊2:
如MSDN文档中所述:"System.Threading.Timer是一个简单的轻量级计时器,它使用回调方法,并由线程池线程提供服务." 对我来说,"轻量级"和"线程池线程"似乎是不兼容的概念.
速聊3:
确实有些奇怪,当在Sleep()和计时器调用之间进行选择时,一些开发人员似乎坚持认为额外的线程间通信和上下文更改是首选解决方案.
速聊4:
我相信实际的(非托管)实现确实使用了增量队列.但是,如果您有很多Delay电话,那么您将创建大量垃圾,那将不是最有效的解决方案.如果您的"成千上万的实体"每秒都在做几件事,那么我会考虑替代方法.
速聊5:
.NET计时器实现包装Win32计时器队列,该队列是触发线程池上事件的增量队列.此处的更多信息:msdn.microsoft.com/en-us/library/ms686796%28v=VS.85%29.aspx

I am thinking on using C# async\await in MMO game server with event-driven logic. Let's assume there are thousands of entities doing some work with known durations. So I would like to invoke Time.Delay() for every of my game objects. (This is an opposit approach to common infinite loop with some Update() call for every game object.)

Does anybody knows how is Task.Delay() implemented? Is it using timers? Is it heavy on system resources?

Is it okay to spawn thousands of simultaneous Task.Delay() invocations?

Talk1:
Because Thread.Sleep isn't async?
Talk2:
And Task.Delay is? That doesn't make sense. If I want to delay a thread, I want it NOW, not spawning another thread to tell one to halt.
Talk3:
The point is to make a thread wait some time before resuming. The point isn't to delay a thread.
Talk4:
The await/async stuff causes a call to Task.Delay() to appear to return immediately from the code that calls it, and the code following the delay will resume after the delay is over. It's non-blocking (as long as the method calling it is itself async).
Talk5:
'sNotALie. because calling Thread.Sleep with argument other than 0 or 1 is always a big design problem.
Solutions1

Task.Delay is implemented as follows:

public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
{
  //error checking
  Task.DelayPromise delayPromise = new Task.DelayPromise(cancellationToken);
  if (cancellationToken.CanBeCanceled)
    delayPromise.Registration = cancellationToken.InternalRegisterWithoutEC((Action<object>) (state => ((Task.DelayPromise) state).Complete()), (object) delayPromise);
  if (millisecondsDelay != -1)
  {
    delayPromise.Timer = new Timer((TimerCallback) (state => ((Task.DelayPromise) state).Complete()), (object) delayPromise, millisecondsDelay, -1);
    delayPromise.Timer.KeepRootedWhileScheduled();
  }
  return (Task) delayPromise;
}

It definitely uses timers. They're used in a class called DelayPromise. Here's the implementation for that:

private sealed class DelayPromise : Task<VoidTaskResult>
{
  internal readonly CancellationToken Token;
  internal CancellationTokenRegistration Registration;
  internal Timer Timer;

  internal DelayPromise(CancellationToken token)
  {
    this.Token = token;
  }

  internal void Complete()
  {
    if (!(this.Token.IsCancellationRequested ? this.TrySetCanceled(this.Token) : this.TrySetResult(new VoidTaskResult())))
      return;
    if (this.Timer != null)
      this.Timer.Dispose();
    this.Registration.Dispose();
  }
}

It does use a timer, but it doesn't seem like a worry to me. The timer just calls back to the complete method, and what that does is check if it's canceled, if so cancel it, else just return a result. It seems fine to me.

Talk1:
I'm actually surprised that it creates a new timer object for each Delay. I would have thought it would use some delta-queue with one timer.
Talk2:
As said in MSDN documentation: "System.Threading.Timer is a simple, lightweight timer that uses callback methods and is served by threadpool threads." To me "lightweightness" and "threadpool threads" seem to be incompatible concepts.
Talk3:
It is indeed a little strange that, when there is a choice between Sleep() and a timer call, some developers seem to insist that extra inter-thread comms and context-changes is the preferred solution.
Talk4:
I believe that the actual (unmanaged) implementation does use a delta-queue. Still, if you have many, many Delay calls, you'll be creating a lot of garbage and it wouldn't be the most efficient solution. If your "thousands of entities" are doing several things a second, I'd consider alternatives.
Talk5:
The .NET Timer implementation wraps the Win32 timer queue, which is a delta-queue that fires events on the thread pool. More info here: msdn.microsoft.com/en-us/library/ms686796%28v=VS.85%29.aspx
转载于:https://stackoverflow.com/questions/17378548/what-it-costs-to-use-task-delay

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入