1

Let's say I have an application that has two tasks running.

    TriggerHour1 = new TimeSpan(0, 1, 0);
    Task.Run(async () => 
    {
        while (true)
        {
            var triggerTime = DateTime.Today + TriggerHour1 - DateTime.Now;
            if (triggerTime < TimeSpan.Zero)
                triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));
            await Task.Delay(triggerTime);
            Console.WriteLine("RunningTask1 finished")
        }
    });

    TriggerHour2 = new TimeSpan(0, 1, 0);
    Task.Run(async () => 
    {
        while (true)
        {
            var triggerTime = DateTime.Today + TriggerHour2 - DateTime.Now;
            if (triggerTime < TimeSpan.Zero)
                triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));
            await Task.Delay(triggerTime);
            Console.WriteLine("RunningTask2 finished")
        }
    });

What happenes if we have a CPU with only one core? Will only one task run? Will the second tasks wait for the first thread to be available?

Edit: Let's say that Tasks 2 relies on Task 1 to be completed first. Will this always be the case on a single Core CPU?

    int counter = 0;
    TriggerHour1 = new TimeSpan(0, 1, 0);
    Task.Run(async () => 
    {
        while (true)
        {
            var triggerTime = DateTime.Today + TriggerHour1 - DateTime.Now;
            if (triggerTime < TimeSpan.Zero)
                triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));
            await Task.Delay(triggerTime);
            Console.WriteLine("RunningTask1 finished");
            counter += 100;
            Console.WriteLine(counter);
        }
    });

    TriggerHour2 = new TimeSpan(0, 1, 0);
    Task.Run(async () => 
    {
        while (true)
        {
            var triggerTime = DateTime.Today + TriggerHour2 - DateTime.Now;
            if (triggerTime < TimeSpan.Zero)
                triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));
            await Task.Delay(triggerTime);
            Console.WriteLine("RunningTask2 finished");
            Console.WriteLine(counter);
        }
    });

Will the first Task always run before?

ehtio
  • 179
  • 3
  • 11
  • 3
    Windows uses a [thread scheduler](https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling) to ensure that [multiple threads](https://learn.microsoft.com/en-us/dotnet/standard/threading/scheduling-threads) can all *appear* to run at once, even if the number of threads exceeds the number of processors. It does this by letting each thread run for a short while before pre-empting it and allowing a different thread to run. (Technically things are more complicated and threads get preempted even if the number is less than the number of processors, but you get the idea.) – Matthew Watson Jul 27 '23 at 10:02
  • 2
    You have many tasks all time. Open Task Manager and see them. Maybe you expect that you have the same number of cores? – i486 Jul 27 '23 at 10:02
  • 2
    You realize there are already a couple of thousand threads running on your computer even before you start your program? – GSerg Jul 27 '23 at 10:03
  • 1
    having said this threads are not neccessarily diretcly related to CPU-cores. YOu can have more threads then cores on your machine. In the other hand tasks aren't related to threads unless you use `Task.Run`. – MakePeaceGreatAgain Jul 27 '23 at 10:05
  • 1
    "Thread" and "Thread" are two different things ... – Fildor Jul 27 '23 at 10:06
  • I understand that. But which thread has priority? The one created first? Let's say the second Taks does something that relies on the first Tasks to be completed first. Could the second Task be executed before? – ehtio Jul 27 '23 at 10:07
  • 2
    `But which thread has priority?` Read the links I posted. – Matthew Watson Jul 27 '23 at 10:07
  • 2
    "Could the second Tasks be executed before?" - absolutely. – Fildor Jul 27 '23 at 10:08
  • 1
    If you're concerned about dependencies between your tasks, you should chose an architecture that allows for this kind of coupling. Threads aren't the correct thing for that as mentioned already. Of course there are scenarios where threads do exactly what you want, but that's not what they are intended for. So the actual question here is: why do you even spwan two threads, when you actually just want to describe two tasks, meaning two difefrent pices of code that should run *one after the other*? – MakePeaceGreatAgain Jul 27 '23 at 10:38
  • 1
    TLDR: Most operating systems schedule threads _preemtively._ That means that it periodically—typically many times per second—checks to see if any thread is waiting to use a CPU, and if so, it kicks some running thread off its CPU _with no warning,_ and it gives the CPU to the waiting thread. If more than one thread is eligible to run, then in the simplest case, it chooses the one that's been waiting longest. – Solomon Slow Jul 27 '23 at 11:48

1 Answers1

5

First of all Task does not always mean there is a thread (see the There Is No Thread by Stephen Cleary), actually in this case both in theory can be served by one thread (due to await Task.Delay(triggerTime)).

As for threads on single core CPU - multithreading can be implemented on a single processor system, it is up for the OS how to schedule them, from Can multithreading be implemented on a single processor system?:

In a single-processor system, multiple threads execute , one after the other or wait until one thread finishes or is preempted by the OS , depending on the thread priority and the OS policy.But the running threads , gives an illusion that they run simultaneous , relative to the required application response time of the User space application.

Also there are some hardware implementations like Intel's Hyper-threading which allows simulating multiple (usually 2) logical cores per one physical.

Could the second Tasks be executed before?

Yes.

Let's say that Tasks 2 relies on Task 1 to be completed first. Will this always be the case on a single Core CPU?

If you have interdependencies between two parallel processes (threads, tasks, whatever) you need to use appropriate synchronization (see the overview of synchronization primitives), there are too many moving parts which can affect thigs - thread pool, OS scheduler, in your case system clock, in some cases memory model (How does CPU reorder instructions, memory ordering)

For example counter += 100; is not a thread safe operation and if multiple threads perform updates to counter you can encounter quite unexpected results.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    Thank you for the thorough answer and for the links! Time to do some reading. – ehtio Jul 27 '23 at 10:50
  • 1
    @ehtio was glad to help! – Guru Stron Jul 27 '23 at 17:03
  • 2
    Hyperthreading, and other fine-grained SMT like AMD Zen and IBM's POWER cpus, is a multi-core system from a software perspective. (https://en.wikipedia.org/wiki/Temporal_multithreading). The front-end alternates between logical cores every cycle, but out-of-order exec in the back-end runs instructions from each logical core at the same time. "Simulating" makes it sound fake. You might say that about coarse-grained multithreading that only switches on stalls (like on cache miss), but that would still get treated by an OS as a 2-core machine. – Peter Cordes Jul 27 '23 at 17:06