10

I am currently reading this excellent article on threading and read the following text:

Thread.Sleep(0) relinquishes the thread’s current time slice immediately, voluntarily handing over the CPU to other threads.

I wanted to test this and below is my test code:

static string s = "";

static void Main(string[] args)
{
    //Create two threads that append string s
    Thread threadPoints = new Thread(SetPoints);
    Thread threadNewLines = new Thread(SetNewLines);

    //Start threads
    threadPoints.Start();
    threadNewLines.Start();

    //Wait one second for threads to manipulate string s
    Thread.Sleep(1000);

    //Threads have an infinite loop so we have to close them forcefully. 
    threadPoints.Abort();
    threadNewLines.Abort();

    //Print string s and wait for user-input
    Console.WriteLine(s);
    Console.ReadKey();
}

The functions that threadPoints and threadNewLines run:

static void SetPoints()
{
    while(true)
    {
        s += ".";
    }
}

static void SetNewLines()
{
    while(true)
    {
        s += "\n";
        Thread.Sleep(0);
    }
}

If I understand Thread.Sleep(0) correctly, the output should be something like this:

............        |
..............      |
................    | <- End of console
..........          |
.............       |
...............     |

But I get this as output:

....................|
....................|
....                |
                    |
                    |
....................|
....................|
.................   |
                    |

Seeing as the article mentioned in the beginning of the post is highly recommended by many programmers, I can only assume that my understanding of Thread.Sleep(0) is wrong. So if someone could clarify, I'd be much obliged.

Jordy
  • 1,816
  • 16
  • 29
  • What language is this that looks a lot like C# but doesn't require a semicolon after statements that manipulate strings? – Ben Voigt Jun 28 '13 at 13:25
  • My mistake, I retyped my code from my workstation without internet. But as the tag shows, this is C# indeed ;) – Jordy Jun 28 '13 at 13:27
  • 4
    Not sure what this code is supposed to prove, other than that reading and writing an object reference in more than one thread without locking has a highly unpredictable outcome. Your test code just isn't valid and the required locking will disprove anything about Sleep(0). – Hans Passant Jun 28 '13 at 13:32
  • @HansPassant I thought that once you call Thread.Sleep(0) it instantly switches to another thread. Making SetNewLines() impossible to be called twice in a row. – Jordy Jun 28 '13 at 13:35
  • 1
    You don't know how the execution of different threads will be spliced. If you want to control that then your code needs to be synchronised in some way. When you call `Thread.Sleep(0)` maybe another thread will get as far as writing to the screen maybe not. – Jodrell Jun 28 '13 at 13:37
  • 5
    No, Sleep(0) does provide any guarantee that it switches to another thread. It allows the thread scheduler to pick another thread. It is very common to pick the same thread again since it is the only one that's ready to run. You would have to find a machine that has only one processor core to prove your point. Very hard to find these days. – Hans Passant Jun 28 '13 at 13:40
  • @HansPassant: Could you prove it by setting the processor affinity? Just something i remembered out of the blue.... – Luis Filipe Jun 28 '13 at 15:05
  • You're trampling over s on multiple threads. You are likely missing lots of updates. Not all writes will show up. I'm not convinced this is a well thought-out experiment :) Not sure what it shows. – usr Jul 23 '13 at 09:54

5 Answers5

2

What thread.sleep(0) is to free the cpu to handle other threads, but that doesn't mean that another thread couldn't be the current one. If you're trying to send the context to another thread, try to use some sort of signal.

saamorim
  • 3,855
  • 17
  • 22
  • Ah, I see. I assumed there was some sort of waiting list, and once a thread had a time slice it would start back in line. But you're saying the next thread the processor handles is (more-or-less) random? – Jordy Jun 28 '13 at 13:25
  • 1
    The next thread to run shouldn't be the current one, if there are other runnable threads of equal priority. But it could be a thread in some other process. Or, you might have a CPU with enough cores to run all your threads at once. – Ben Voigt Jun 28 '13 at 13:27
  • 1
    @saamorim, In practice, I think it's unlikely that's what's happening here. I think it's much more likely a caching issue. – Dan Bryant Jun 28 '13 at 13:27
  • Regardless of the method the OS uses to schedule thread operations (or the CLR), I would think that you couldn't depend on the implementation never changing, as it's out of your control. – Steve Jun 28 '13 at 14:05
1

If you have access to a machine (or perhaps a VM) with only a single core/processor, try running your code on that machine. You may be surprised with how the results vary. Just because two threads refer to the same variable "s", does not mean they actually refer to the same value at the same time, due to various levels of caching that can occur on modern multi-core (and even just parallel pipeline) CPUs. If you want to see how the yielding works irrespective of the caching issues, try wrapping each s += expression inside a lock statement.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • I tried locking and the results are different (more new lines anyway), but it still doesn't quite seem like what I would expect. Gotta find myself a single core processor.. – Jordy Jun 28 '13 at 13:33
1

If you'd expand the width of the console to be 5 time larger than current then you'd see what you expect, lines not reaching the console width. The problem is one time slice is actually very long. So, to have the expected effect with normal console with you'd have to slow down the Points thread, but without using Sleep. Instead of while (true) loop try this

for (int i = 0;; i++)
{
  if (int % 10 == 0)
    s += ".";
}

To slow down the thread even more replace number 10 with bigger number.

Dialecticus
  • 16,400
  • 7
  • 43
  • 103
0

The next thread the processor handles is random thread and it even could be the same thread you just called Thread.Sleep(0). To ensure that next thread will be not the same thread you can call Thread.Yield() and check it's return result - if os has another thread that can run true will be returned else false.

Andriy Vandych
  • 144
  • 1
  • 6
-1

You should (almost) never abort threads. The best practice is to signal them to die (commit suicide).

This is normally accomplished by setting some boolean variable and the threads should inspect its value to whether continue or not its execution.

You are setting a string variable named "s". You will incur in race conditions. String is not thread safe. You can wrap the operations that manipulate it in a lock or use a built-in type that is thread-safe.

Always pay attention, in the documentation, to know if the types you use are thread-safe.

Because of this you can't rely on your results because your program is not thread-safe. If you run the program several times my guess is that you'll get different outputs.

Note: When using a boolean to share some state to cancel threads, make sure it is marked as volatile. JIT might optimize the code and never looks at its changed value.

Luis Filipe
  • 8,488
  • 7
  • 48
  • 76
  • 3
    `string` is thread-safe. The way he's using his `string` handles, not so much. – Ben Voigt Jun 28 '13 at 13:23
  • Getting the string value and writing some new value to it is not thread-safe, as far as i know – Luis Filipe Jun 28 '13 at 13:24
  • 3
    That's not an operation on a `string` object, it creates a brand new `string`. – Ben Voigt Jun 28 '13 at 13:25
  • In this situation, I purposely start a race condition, to get some sort of random wave of dots. But you're saying that once it's threadsafe nothing can be as expected? Because the pattern is pretty much the same whenever I rerun the code. – Jordy Jun 28 '13 at 13:30
  • 1
    `CancellationToken` comes to mind http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx – Jodrell Jun 28 '13 at 13:44
  • Two threads get the string value at the same time. The one who writes it latest will override the previous write. Please, tell me if i am wrong – Luis Filipe Jun 28 '13 at 15:00
  • [This article](http://msdn.microsoft.com/en-us/magazine/jj863136.aspx) has a lot of interesting details on the C# memory model as defined by ECMA-334. 'Latest' is a tricky concept when you have parallel processing going on. – Dan Bryant Jun 28 '13 at 15:21
  • @Luis: Two threads get the string value at the same time. Each copies it to a new larger string, appending either `.` or `\n` respectively in the process. And then each overwrites the original string handle with a reference to his own newly created longer string. The original string is not modified. Nor do you ever have two threads writing to the same string. – Ben Voigt Jun 28 '13 at 23:56
  • @BenVoigt: But the reference the variable "s" has to the string is to the one who wrote it last, right? Either the . or the \n will be lost. – Luis Filipe Jun 30 '13 at 08:58