Why do we need to add sleep in a thread?

If you are using any thread in your application, and your application seems NOT work as expected. The first thing should be checked is, is there a thread.sleep(x) called? if no, please add it.

if you want to know why, run the small example below:

  • For the first run, keep Thread.sleep(1) in Thread1. Look at your oscilloscope, the signal is toggled every 1ms, as we expected.
  • For the second run, disable Thread.sleep(1) in Thread1. Look at your oscilloscope, now, it is 20ms or different, but not 1ms any more.

This issue can be reproduced with any NETMF devices, and I believe that some people already know, but sometime we forget it. :))

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
namespace Test_ThreadWithoutSleep
{
    public class Program
    {
        
        public static void Main()
        {
            new Thread(Thread2).Start();
            new Thread(Thread1).Start();
            Thread.Sleep(-1);
        }
       static void Thread1()
        {
            while (true)
            {               
                Thread.Sleep(1);
            }
        }
        static void Thread2()
        {
            OutputPort digitalsignal = new OutputPort((Cpu.Pin)(x), true); 
            while (true)
            {
                digitalsignal.Write(!digitalsignal.Read());
                Thread.Sleep(1);
            }
        }
        

    }
} 

It may not help with your current issues, but your code will run correctly and smoother.

:dance:

5 Likes

If your oscilloscope allows to make screen captures, could you please upload them? A picture is worth a thousand words.

I believe this is not really related to NETMF but to a multithreading on single-core MCU itself.

There is one more example: if you want to load some big file from Sd card in a background thread than you shall not use File.ReadAllBytes() because it will lock your main thread as well. Instead you should use someshing like:
While not EOF
{
Read small chunk (256 bytes)
Sleep
}

2 Likes

@ Dat - That is a nice way to show what is happening. It might also be worth explaining the mechanics here, esp. Since it is documented and consistent with how the Win32 APIs and by extension he desktop .NET Framework work.

Each thread is allocated a 20ms timeslice during which time all other threads waiting to execute are placed in a ready queue based on priority etc. Given this scheduling strategy, a thread could experience execution delays of no less than 20ms, but this time can increase linearly with the number of threads that are in the ready state. And it could be even worse if complex thread priorities are involved.

One way that a developer can exercise some level of control is to assign your time critical thread a higher priority. But this does require that you have a good understanding of your application’s concurrency characteristics otherwise you risk staving lower priority threads.

On other way to exert some control over the scheduler is to yield your timeslice as soon as you are done doing work rather than allowing the thread to execute for the full 20ms. To yield your timeslice to the scheduler you can cal Thread.Sleep(0) which will yield the remainder of the time slice and immediately return your thread to the ready queue to be scheduled again. Calling Thread.Sleep with a value higher than 0 will yield the balance of the the threads timeslice back to the scheduler and the delay the thread being added to the ready queue by the specified number of milliseconds.

Of course the other side of it is that yielding the threads timeslice early also means that the thread takes longer to work through the workload, for example if your workload is a loop that would have run to completion in 10ms, but you yield after every iteration it will now take significantly longer to complete the loop.

That is not a detailed explanation of the process, there are fine details around priorities etc. but this covers the most common case in which all threads are running at equal priority.

5 Likes

@ Dat - What does " your application seems NOT work as expected" mean? The code you posted is fine. The problem is that there will be difficulty attaching the debugger to the program.

Inserting the sleep in your program will reduce the frequency of the output signal significantly.

Inserting a Sleep into a program to “make it work as expected” is not good practice unless you fully understand why the Sleep was required.

2 Likes

Sleep just gives other threads a chance to operate.

There is no DoEvents() in NetMF - this is pretty much an equivalent.

VB6 did indeed call the Win32 API Sleep which works pretty much like I described in the earlier post.

However, the VB.NET implementation merely processes any pending events (pumps the message queue) but does not force a context switch so the thread still runs for the full time slice. This allows you to have the UI update while in a tight loop in the very same UI thread. Terrible practice of course, you should use threads here, but it is a hold over from the days of Windows 3.x which was cooperatively multitasked and pumping the message queue was the right thing to do if you wanted to be a good citizen in the Windows eco-system. And to maintain backward compatibility and ensure the best chance of success when porting earlier single task applications to later versions of VB/VB.NET.

I never programmed in VB, but if I am not mistaken threads where also not natively supported in VB6 so DoEvents was still relevant. But take this last statement with a grain of salt.

Calling Sleep() merely yields your timeslice to other threads; that is to say, if you have one thread that is doing a lot of work, calling Sleep() on another thread is a round-about way to give it a bigger share of the available time.

Advising people to do this, in my opinion, is a bad idea. There is no way to “magically” invent more CPU time, at the end, you must pay the piper. Furthermore, creating another thread that just calls Sleep() in a loop adds more overhead, that is to say, there will be less overall time available to do useful work, because the scheduler will be busy waking up your thread so it can just be put to sleep again.

What we REALLY need is overlapped I/O :wink:

2 Likes

Just banging my head against the wall for not thinking of this. :wall:

Great tip. Thanks.