55

I wrote this little program:

class Program
{
    static void Main(string[] args)
    {
        Thread t = new Thread(WriteX);
        t.Start();

        for (int i = 0; i < 1000; i++)
        {
            Console.Write("O");
        }
    }

    private static void WriteX()
    {
        for (int i = 0; i < 1000; i++)
        {
            Console.Write(".");
        }
    }
}

I ran it about fifty times, and the first character on the console was always "O". It is weird for me, because the t thread starts first then the main continues.

Is there any explanation for this?

Mat
  • 202,337
  • 40
  • 393
  • 406
KOB
  • 1,156
  • 1
  • 16
  • 33
  • 51
    (un)luck. There is no guarantee that the O will be first, but overhead when creating the secondary thread will make it very unlikely that the second thread will end up racing to the finish line first. You can compare this with standing 20cm from the finish line, then yelling "Gentlemen, start your engines and go!" and then immediately take a step over the finish line. The chance is present, but negligible. – Lasse V. Karlsen Jun 01 '15 at 07:26
  • 1
    The behavior isn't deterministic and depends on the OS thread scheduler. Don't forget that creating the thread itself has overhead. Try running it enough times and you'll see it in different variations. – Yuval Itzchakov Jun 01 '15 at 07:27
  • 1
    It could relate to the time it takes the thread to start up (in that time the mainthread continues its own work...aka it starts the for loop). Did you try what happens when you put a short sleep after t.Start() in the main (about 30 milliseconds for example)? – Thomas Jun 01 '15 at 07:27
  • Threads are managed by the OS in terms of when they are given a time-slice to run in. Plus there is also a start-up cost of a thread, whereas your remaining code only has a small handful of instructions to do to print `O`. You don't get to control this sort of thing really. You can do thread synchronisation and such, but it's usually better to work with this idea of things running arbitrarily in relation to each other than enforce an order (big topic these days, event-driven systems, actors, concurrent collections, etc etc) – Adam Houldsworth Jun 01 '15 at 07:27
  • There is no such thing as "t starts first", it's not like the thread starts executing your code right away. There is overhead in creating a thread, but this overhead is mostly done on the start of that second thread. As such, while the second thread is still spinning up, the main thread continues to execute. – Lasse V. Karlsen Jun 01 '15 at 07:28
  • Running your code around 20 times and twice output started from "." – Uriil Jun 01 '15 at 07:35
  • Windows is not a realtime operating system, ie it is not deterministic. – Philip Stuyck Jun 01 '15 at 07:46
  • 1
    @Uriil Yet, (s)he might never came to that result. i.e. On Intel atom-based architectures creating a background thread is a tiny bit more expensive then on the i5/i7 architecture, so you will see a quite different statistical result. (*Don't ask! Just be happy because you should not know why. :D* ) – mg30rg Jun 01 '15 at 08:10
  • @Thomas Putting a (Thread).Sleep() in the code is not a real solution. The main thread already has a nondeterministic heads-up (because of the overhead of the thread creation), with (Thread).Sleep() you are giving a **deterministic** (at least 30ms) **overhead** to the background thread, so at the end of the day you will still have a main thread with a kind of constant runtime and a background thread with a non-deterministic runtime. What needs to be done is a synched start of the parallel function like [the one in my ~answer](http://stackoverflow.com/a/30568340/2144232). – mg30rg Jun 01 '15 at 08:57
  • A thread scheduler works by allotting CPU core time slices to active threads. When you start a thread, it essentially only means that the thread is declared to the OS thread scheduler as active one, i. e. a valid candidate to switch to. All currently scheduled threads are likely to continue running until their allotted time slices expire. Unless the new thread has a higher priority than one of the currently executing threads, the thread scheduler will not preempt any of them prematurely. – ach Jun 01 '15 at 09:52
  • @LasseV.Karlsen: that comparison is simply awesome! That should have been the accepted answer =) –  Jun 01 '15 at 11:07
  • @LasseV.Karlsen, even if the second thread requires some overhead to start, it's just one of the many factors that can affect thread scheduling. If the system was heavily loaded right after the second thread started, the scheduler could still schedule the second thread so it can initialize and run while the main thread remains paused. In my opinion, non-deterministic behaviour cannot be explained with deterministic reasoning. – sleblanc Jun 01 '15 at 14:43
  • 1
    And in addition threads [may behave differently](http://stackoverflow.com/questions/10748397/infinite-loop-in-release-mode) on debug and release mode like for this fellow, though it's a different case it worths knowing – MVCDS Jun 02 '15 at 20:50
  • Never forget the most important advice about the `thread` class. Which is, by the way, "**never use the `Thread`class**". there are so many alternatives (`ThreadPool`, `Task`...) and so little cases where `new Thread(*whatever*)` is really the best approach that you shouldn't bother with it unless you know very *very* well what you're doing, or if you're just curious. – Falanwe Jun 02 '15 at 23:31
  • @Falanwe I guess KOB made this code out of pure curiosity. (I mean, I don't see any real life use for this except for a quite slow, complicated and heavy-weight entropy-source.) – mg30rg Jun 03 '15 at 09:47
  • @mg30rg It is true. I am just testing the capability of the language and the framework. And I can understand now (thanks to all of you) the multithreading is not a random thing (first character is 50% "." and 50% "O" on a long run). It is a total not guaranteed thing. Even 1000 dot next each other and 1000 "O" after that is also possible every time the code runs on some machines. – KOB Jun 04 '15 at 08:10
  • 1
    @KOB Please also note that - by putting console output in your threads - you share a resource between your threads which causes a **race** for obtaining the console (which - in C# - is a somewhat serialized resource), so it is not the best way to test which thread gets more processing time. Storing the system time in millisec's in two separate lists in each of your threads, then printing a string based on that list would give you a quite different result (Was that clear? I'm not native in English.) – mg30rg Jun 04 '15 at 08:28

7 Answers7

53

This is probably because Thread.Start first causes the change of state of thread on which it is called and OS schedules it for execution whereas the main thread is already running and does not need these two steps. This is probably the reason that the statement in main thread executes first rather the one in the newly created thread. Keep in mind the sequence of thread execution is not guaranteed.

Thread.Start Method

1) Thread.Start Method Causes the operating system to change the state of the current instance to ThreadState.Running.

2) Once a thread is in the ThreadState.Running state, the operating system can schedule it for execution. The thread begins executing at the first line of the method represented by the ThreadStart

Edit It seems to me that representing this in graphical form will make this more clear and understandable. I tried to show the sequence of thread execution in diagram below.

enter image description here

Adil
  • 146,340
  • 25
  • 209
  • 204
20

You say:

"It is weird for me, because the t thread starts first then the main continues.".

This is not true. The "main" tread is already running. When t.Start(); is executed, the OS is told t is in the running state. The OS will then schedule execution time for the thread "soon". This is something else than the OS is instructed to stop execution of this thread until thread t is started. In other words, when Start returns, there is no guarantee that the thread has already started executing.

Martijn
  • 11,964
  • 12
  • 50
  • 96
  • 1
    Short and on the spot. The thread is *allowed* to be started, and `Start` doesn't *wait* for it to actually start executing. – Luaan Jun 01 '15 at 12:21
13

More of an advice than not an answer:

(Please note, that I see no real-life use for what you are trying to achieve, so I treat your problem as a thought experiment/proof of a concept not explained in detail.)


If you want your threads to "race" for control, don't give your main thread a head start! Creating a thread has some overhead and your main thread is already created (since it creates your other thread). If you are looking for a mostly equal chance for both of your main and worker thread, you should wait for your worker thread to be created in the main thread and wait for the main thread to start the race in your background thread. This can be achived by synch objects.


In practice it would look like this:

You should declare two ManualResetEvents which are visible for both your main- and background thread like this:

private static ManualResetEvent backgroundThreadReady = new ManualResetEvent(false);
private static ManualResetEvent startThreadRace = new ManualResetEvent(false);

Then in your main thread, you should wait for your thread being initialized like:

static void Main(string[] args)
{
    Thread t = new Thread(WriteX);
    t.Start();
    backgroundThreadReady.WaitOne(); // wait for background thread to be ready

    startThreadRace.Set();           // signal your background thread to start the race
    for (int i = 0; i < 1000; i++)
    {
        Console.Write("O");
    }
}

And in your thread:

    private static void WriteX()
    {
        backgroundThreadReady.Set(); // inform your main thread that this thread is ready for the race

        startThreadRace.WaitOne();   // wait 'till the main thread starts the race
        for (int i = 0; i < 1000; i++)
        {
            Console.Write(".");
        }
    }

Please note that I could have used other waitable sync objects (mutex, autoreset event, even a critical section lock with some hack, I've just choose the simplest, fastest solution which can be extended easily).

mg30rg
  • 1,311
  • 13
  • 24
5

Your code is non deterministic. Your code contains no thread primitives that would schedule priority of one thread over another or for one thread to wait for another.

Kevin
  • 2,281
  • 1
  • 14
  • 16
4

It basically needs time to start the thread up. You are running the thread code at the same time as the rest of the first method. So taking into account the time it takes to start the thread and then get to the point where it is writing the "." does that make sense?

If you have a sort of reset button in your app to start everything again (without exiting) you may find that the first character is the "." because the thread will already exist.

Keithin8a
  • 961
  • 7
  • 32
  • 1
    I'm not sure if your thread caching statement is accurate. My understanding was manually created threads don't pilfer or add to the thread pool. To use a thread pool thread you need to either use TPL and the current scheduler, or the older `ThreadPool` stuff. – Adam Houldsworth Jun 01 '15 at 07:32
  • Thanks for that, I have updated it now (hopefully to something correct). To be fair I have just started to experiment with Multithreading and TPL and I just observed that behaviour and assumed it was caching it. I guess it makes sense that it is already there so it doesn't need the overhead of starting it again. – Keithin8a Jun 01 '15 at 07:38
4

Main process continue its next instructions set after invoking the thread ,It will take time to start thread method as light process.

Venkata Naidu M
  • 351
  • 1
  • 6
-1

There is only one reason why the main thread will finish before the created thread and that is because it takes time to start a thread. The only time you would use threads to speed up a program is when 2 tasks can be run at the exact same time. If you want to make the second loop finish first , take a look at Parallel.For loops in c#... these will run each loop in the for loop at the same time (not all of them but as much as your PC can handle)

Brendon Vdm
  • 914
  • 5
  • 5