Can Task.Delay cause thread switching? [duplicate]
Asked Answered
L

1

6

I have a long running process that sends data to the other machine. But this data is received in chunks (like a set of 100 packets, then delay of minimum 10 seconds).

I started the sending function on a separate thread using Task.Run(() => { SendPackets(); });

The packets to be sent are queued in a Queue<Packet> object by some other function.

In SendPackets() I am using a while loop to retrieve and send (asynchronously) all items available in the queue.

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
    }
 }

All the locks are in place!

My question is, when I use Task.Delay, is it possible then the next cycle may be executed by a thread different from the current one?

Is there any other approach, where instead of specifying delay for 1 second, I can use something like ManualResetEvent, and what would be the respective code (I have no idea how to use the ManualResetEvent etc.

Also, I am new to async/await and TPL, so kindly bear with my ignorance.

TIA.

Lotti answered 6/3, 2020 at 8:22 Comment(1)
If you're trying to use thread state or otherwise need thread affinity you're probably doing things wrong to start with. Unfortunately, there's not enough code here for us to see where you're trying to depend on thread affinity. Also, which Queue are you using?Partin
A
11

My question is, when I use Task.Delay, is it possible then the next cycle may be executed by a thread different from the current one?

Not with the code you've got, because that code is buggy. It won't actually delay between cycles at all. It creates a task that will complete in a second, but then ignores that task. Your code should be:

await Task.Delay(1000);

or potentially:

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

With the second piece of code, that can absolutely run each cycle on a different thread. With the first piece of code, it will depend on the synchronization context. If you were running in a synchronization context with thread affinity (e.g. you're calling this from the UI thread of a WPF or WinForms app) then the async method will continue on the same thread after the delay completes. If you're running without a synchronization context, or in a synchronization context that doesn't just use one thread, then again it could run each cycle in a different thread.

As you're starting this code with Task.Run, you won't have a synchronization context - but it's worth being aware that the same piece of code could behave differently when run in a different way.

As a side note, it's not clear what's adding items to thePacketQueue, but unless that's a concurrent collection (e.g. ConcurrentQueue), you may well have a problem there too.

Algicide answered 6/3, 2020 at 8:27 Comment(5)
They've shown they're using Task.Run to start (the code they've shown) running. I think that means no sync context.Partin
@Damien_The_Unbeliever: Yup, will edit.Algicide
Could SpinWait be used as well? To my understanding, that would even allow you to provide a predicate to evaluate exactly when it should stop spinning, and also prevent context switching, which could be rather expensive depending on the scenario. learn.microsoft.com/en-us/dotnet/standard/threading/spinwaitOrlena
@marcofo88: I doubt that SpinWait is an appropriate option here. If the OP really needs to stay on the same thread, Thread.Sleep would probably be the simplest option.Algicide
@Orlena SpinWait / Remarks: "SpinWait is not generally useful for ordinary applications. In most cases, you should use the synchronization classes provided by the .NET Framework...". SpinWait is intended for use in low-level code.Ramsdell

© 2022 - 2024 — McMap. All rights reserved.