Does Arduino need a real-time scheduler?

I first started with Netduino, then switched to Arduino because of more hardware and components and also cheaper, I'm now fiddling with some RF components and I really really need Threads, I don't know exactly what threads are in .NET and what is the importance of RTOS. But I need precision timing, and execution from a couple of wirelessly enabled arduino units. Think of it as a wireless sequential light system, I messed with millis(), timers and lots of libraries, all are unpredictable and hang. Or just drift apart as time goes by in execution. I dunno maybe im doing something wrong, but when I used Netduino I could do this preety easily.

Anyway its explained like this:

The .NET Micro Framework CLR has only one thread of execution and owns all of the memory in the system. During normal operation, the CLR iterates in the interpreter loop and schedules managed threads using a round-robin algorithm, according to the priority of the threads involved. Each managed thread gets a 20-millisecond (ms) time quantum during which the runtime executes intermediate language (IL) code that belongs to the stack of the managed thread being serviced. When the managed thread goes to sleep or waits for a synchronization primitive, such as a monitor that cannot be acquired or an event that is not signaled, the CLR puts the thread in the waiting queue and tries to schedule another thread. In between scheduling managed threads, the CLR checks for any hardware events that might have been raised at the native device driver level. If an event occurred, the CLR tries to dispatch the event to the managed thread that requested it. A hardware event is associated with some kind of input/output (I/O) event, whether it be an interrupt-based GPIO event or a serial USART, USB, or I2C event.

The runtime interpreter loop starts by checking to discover whether a thread is ready for a time slice. If a thread is ready, the CLR schedules the thread for processing and tests to determine whether any hardware interrupt events require processing. If there are, the CLR processes the hardware interrupt event and moves the thread that handles the interrupt into the queue of threads that are ready to be processed. If no hardware interrupts have occurred, the CLR again tests to see whether a thread is ready for a time slice.

If the CLR determines that there are no threads that are ready to be processed, it sets a timeout timer and goes to sleep in order to conserve power. When the timeout timer triggers or an interrupt occurs, it wakes the CLR back up. At that time, the CLR determines whether an interrupt or the timer woke it up. If it was an interrupt, the CLR determines if processing the interrupt is required. If not, it sets the timeout timer and goes back to sleep. If it is the timeout timer that wakes the CLR, the CLR executes any pending completions and checks again for interrupts. If there are waiting interrupts, it goes through the normal interrupt processing procedures. If there are no waiting interrupts, the CLR sets its timeout timer and goes back to sleep.

Basic example with Netduino

namespace NetduinoApplication1
{
    public class Program
    {

        static Thread myThread = new Thread(new ThreadStart(myFunc));
        
        public static void Main()
        {
            myThread.Start();

            //suspend at some point, wait for event
            //myThread.Suspend();

            //resume at some point, wait for event
            //myThread.Resume();

            Thread.Sleep(Timeout.Infinite);
        }

        public static void myFunc() { 
            // do some stuff
        }

    }
}