Task.Delay会引起线程切换吗?

5

我有一个长时间运行的进程,将数据发送到另一台机器。但是这些数据以块的形式接收(例如一组100个数据包,然后最少延迟10秒)。

我使用Task.Run(() => { SendPackets(); });在单独的线程上启动了发送函数。

要发送的数据包由其他功能排队在Queue<Packet>对象中。

SendPackets()中,我使用while循环来检索并异步发送队列中所有可用项目。

void SendPackets()
{
    while(isRunning)
    {
       while(thePacketQueue.Count > 0)
       {
          Packet pkt = thePacketQueue.Dequeue();

          BeginSend(pkt, callback);   // Actual code to send data over asynchronously
       }

       Task.Delay(1000);  // <---- My question lies here
    }
 }

所有锁都已经就位!

我的问题是,当我使用 Task.Delay 时,下一个周期是否可能由与当前线程不同的线程执行?

是否有其他方法,可以使用类似于 ManualResetEvent 的东西,而不是对1秒钟的延迟进行指定,并且相应的代码是什么(我不知道如何使用 ManualResetEvent 等)。

此外,我对异步/等待和 TPL 不熟悉,请谅解我无知。

谢谢您提前。


1
如果您正在尝试使用线程状态或需要线程亲和力,那么您可能从一开始就做错了事情。不幸的是,这里的代码不足以让我们看到您正在尝试依赖线程亲和性的位置。此外,您正在使用哪个Queue - Damien_The_Unbeliever
1个回答

10
我的问题是,当我使用Task.Delay时,下一个循环可能由与当前线程不同的线程执行吗?
根据您提供的代码,不会有这种情况发生,因为该代码存在错误。它实际上不会在循环之间延迟。它创建了一个将在一秒钟内完成的任务,但然后忽略了该任务。您的代码应该是:
await Task.Delay(1000);

或者可能是:

await Task.Delay(1000).ConfigureAwait(false);

使用第二段代码,可以绝对在每个周期上运行不同的线程。而在第一段代码中,这将取决于同步上下文。如果您正在同步上下文中具有线程亲和性(例如,从WPF或WinForms应用程序的UI线程调用此方法),则异步方法将在延迟完成后继续在同一线程上运行。如果您没有同步上下文,或者在并非只使用一个线程的同步上下文中运行,则它也可以在每个周期中运行不同的线程。

由于您使用了Task.Run,因此您将没有同步上下文-但值得注意的是,以不同方式运行相同的代码可能会表现不同。

顺便说一句,不清楚是什么向thePacketQueue添加项目,但除非那是一个并发集合(例如ConcurrentQueue),否则您可能也会遇到问题。


2
他们已经展示了他们正在使用Task.Run来启动(他们展示的)代码运行。我认为这意味着没有同步上下文。 - Damien_The_Unbeliever
1
@Damien_The_Unbeliever:好的,我会编辑。 - Jon Skeet
SpinWait 也可以使用吗?据我所知,这甚至允许您提供一个谓词来准确评估何时应停止旋转,并且还可以防止上下文切换,这取决于场景可能会相当昂贵。https://learn.microsoft.com/en-us/dotnet/standard/threading/spinwait - marcofo88
1
@marcofo88:我怀疑SpinWait在这里不是一个合适的选项。如果OP确实需要留在同一线程上,那么Thread.Sleep可能是最简单的选择。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接