Runtime: Sleep another thread

Created on 14 Apr 2019  路  1Comment  路  Source: dotnet/runtime

@wooden-utensil commented on Fri Apr 12 2019

Why is it possible to __Sleep__ in the _current Thread_, but not a _different Thread_.
The Sleep function is part of the Thread class, but isn't part of _an instance_ of Thread.

namespace Example
{
  class Program
  {
    static void Main(string[] args) {
      Thread.Sleep(1000); // I can sleep in the main thread
      var exampleThread = new Thread(() =>
      {
        Thread.Sleep(1000); // I can sleep in this thread
      });
      exampleThread.Start();
      exampleThread.Sleep(1000); // Why can't I sleep here?
    }
  }
}

@marbergq commented on Fri Apr 12 2019

What would be the usecase for sleeping other threads?


@wooden-utensil commented on Fri Apr 12 2019

What would be the usecase for sleeping other threads?

@marbergq What do you mean? Is it a question?


@gfoidl commented on Sat Apr 13 2019

This is better suited in https://github.com/dotnet/corefx as API Review Process.


@yaakov-h commented on Sat Apr 13 2019

You can .Suspend() and .Resume() another thread but they're dangerous, obsoleted, and not implemented in .NET Core.

However, as @gfoidl suggests, this is part of the base class libraries and not related to the C# language.


@wooden-utensil commented on Sat Apr 13 2019

You can .Suspend() and .Resume() another thread but they're dangerous, obsoleted, and not implemented in .NET Core.

However, as @gfoidl suggests, this is part of the base class libraries and not related to the C# language.

Thanks for the advice, I can't transfer the issue, but I'm sure the people at dotnet would be glad to transfer.
Does that mean the issue is closed or is there an alternative to .Suspend() and .Resume().


@HaloFour commented on Sat Apr 13 2019

@wooden-utensil

Does that mean the issue is closed or is there an alternative

The alternative is to use synchronization primitives like Monitor, Mutex, Semaphore, etc. Suspending another thread is not considered a safe action as you can never know exactly what that other thread is executing. I don't think it's at all likely that the BCL teams would consider adding them back to the runtime.


@wooden-utensil commented on Sat Apr 13 2019

@wooden-utensil

Does that mean the issue is closed or is there an alternative

The alternative is to use synchronization primitives like Monitor, Mutex, Semaphore, etc. Suspending another thread is not considered a safe action as you can never know exactly what that other thread is executing. I don't think it's at all likely that the BCL teams would consider adding them back to the runtime.

I know my thread is never doing anything with files, registry, etc so they can't become corrupted. Does that mean it's still safe? Also is that why it's considered unsafe?


@HaloFour commented on Sat Apr 13 2019

@wooden-utensil

I know my thread is never doing anything with files, registry, etc so they can't become corrupted. Does that mean it's still safe? Also is that why it's considered unsafe?

It's never considered safe. You never know exactly what that thread is doing at any given point in time and given that .NET runs on a VM it could be in the middle of an operation within the VM. The documentation gives two examples of situations that could lead to deadlocks, but those are far from an exhaustive list. The appropriate approach is to coordinate your threads using synchronization primitives, then you control exactly where they would block.


@PathogenDavid commented on Sat Apr 13 2019

(This sort-of turned into a ramble-rant. I hope you find it informative. I swear there's some insight for why using Thread.Suspend is a really bad idea buried in here. If you can't tell, I've been hurt by people who thought arbitrary thread suspension was a good idea. Thanks for helping me procrastinate on my taxes. 馃榿)

Does that mean it's still safe?

If you have to ask: No, it is not. It is basically never safe to suspend sibling threads in your own process. (Sorry for being the "If you have to ask" guy. I hate that response too, but I think this really is a case where it applies.) It is always dangerous to suspend threads period. Hard stop. Do not pass go. Do not collect $200.

Suspending threads is almost always done for the wrong reasons. Here is when it's OK to suspend threads arbitrarily:

  • You are writing the task schedule for an operating system.
  • You are writing an out of process debugger.
  • You have a lot of control over the execution environment and you know enough about what you're doing to write many paragraphs about why it's OK in your context, why you need it, and how you do it. (Side note: threadsuspend.cpp is 7,199 lines long. Pretty spoopy.)

This list is exhaustive.

"I think I have a legitimate use case." You probably don't.

"I wrote a task scheduler for an embedded OS, I know what I'm talking about. I'm making a simulator for that OS that runs on Windows. I'm just adapting that same logic for thread scheduling of OS threads to Windows threads." You're looking at this from an embedded point of view, that won't work how you want it to.

"Hey David, remember when we ignored you a while back. Fun times. Anyway, the simulator locks up constantly, especially the ones that have graphics. Can you fix it?" 馃檭

(In the unlikely event a former coworker finds this: I know I simplified and dramatized a bit there 馃槵)


Also is that why it's considered unsafe?

To demonstrate why suspending an arbitrary thread is unsafe, let's consider the following two threads:

while (true)
{
    Console.WriteLine("Hello, world!");
}

Is it safe to suspend this thread at an arbitrary time?

If you thought: "Of course! It's hello world, the simplest of all programs." You would unfortunately be incorrect.

To understand why, we have to consider how Console.WriteLine is implemented. (This also applies to printf and the underlying OS functionality for writing to consoles that it uses. This whole thing is a computing issue, not a C# one.)

Imagine Console.WriteLine looks like this pseudocode down at the lower levels:

void ConsoleWriteLineInternals(const char* message)
{
    AcquireConsoleBufferLock();
    CopyToConsoleBuffer(message);
    ReleaseConsoleBufferLock();
}

That console buffer lock is used to ensure that if multiple threads write to the console at once, no output is lost. Normally this lock is held for a very short period of time - only as long as it takes to copy the message to the console buffer.

Now imagine we suspend this thread while this lock is held. If another thread comes along and tries to write the console, that thread will have to wait for the lock to be released. Unfortunately it might never be released because the thread which owns the lock is suspended, so the operating system will never schedule it for execution.


Here are full examples in both C# and C++ if you want to run it for yourself to see it lock up: https://github.com/PathogenPlayground/ThreadSuspendIsBad

If you break and look at the main thread in the debugger during the deadlock, the C# one will be stuck in SyncTextWriter.WriteLine since the framework has an internal lock for the console. In the C++ one you'll see it's stuck trying to acquire a lock with RtlEnterCriticalSection. (Note: You need to load symbols for ntdll.dll to see that for the C++ version. Right-click the stack frame and load symbols in Visual Studio.)


If you have more questions or want clarification, feel free to ask. I was a little all over the place there, so it wouldn't surprise me if I left something out and made it confusing as a result.

BTW, Thread.Suspend isn't actually implemented in .NET Core.


@eyalsk commented on Sat Apr 13 2019

With all due respect this repo exists for language design, you might want to check with one of the communities noted here for help.

area-System.Threading

Most helpful comment

This is by design for much of the reasons described above. We recommend you use synchronization primitives.

>All comments

This is by design for much of the reasons described above. We recommend you use synchronization primitives.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

omajid picture omajid  路  3Comments

nalywa picture nalywa  路  3Comments

jamesqo picture jamesqo  路  3Comments

noahfalk picture noahfalk  路  3Comments

bencz picture bencz  路  3Comments