Thread.Sleep(1)是否特殊? [英] Is Thread.Sleep(1) special?

查看:129
本文介绍了Thread.Sleep(1)是否特殊?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Joe Duffy( Windows上的并行编程的作者)用此博客文章表示Thread.Sleep(1 )比Thread.Sleep(0)优先使用,因为它会在相同优先级的线程和较低优先级的线程中挂起,而不仅仅是在优先级相同的线程中挂起.

MSDN的 .NET版本表示Thread.Sleep( 0)是特殊的,它将暂停该线程并允许其他等待线程执行.但这并没有说明Thread.Sleep(1)(对于任何.NET版本).

那么,Thread.Sleep(1)真的有什么特别的地方吗?

背景:

我正在刷新有关并行编程的知识.我编写了一些C#代码,以明显地表明前后的增量和减量是非原子的,因此不是线程安全的.

为避免需要创建数百个线程,我在增加共享变量以强制调度程序运行另一个线程后放置Thread.Sleep(0).这种规则的线程交换使得增加/减少前/后增加/减少的非原子性更加明显.

Thread.Sleep(0)不会引起预期的其他延迟.但是,如果将其更改为Thread.Sleep(1),它似乎会恢复为正常的睡眠行为(例如,我至少会延迟约1毫秒).

这意味着虽然T​​hread.Sleep(1)可能是首选,但在循环中使用它的任何代码运行起来都会慢得多.

这个问题有人可以用Sleep解释这种有趣的行为( 1)?" 有点相关,但是它以C ++为重点,只是重复了Joe Duffy的博客文章中的指导.

这是我对任何有兴趣的代码的代码(从LinqPad复制,因此您可能需要在其周围添加一个类):

int x = 0;

void Main()
{
    List<Thread> threadList=new List<Thread>();
    Stopwatch sw=new Stopwatch();

    for(int i=0; i<20; i++)
    {
        threadList.Add(new Thread(Go)); 
        threadList[i].Priority=ThreadPriority.Lowest;
    }

    sw.Start();

    foreach (Thread thread in threadList)
    {
        thread.Start();
    } 


    foreach (Thread thread in threadList)
    {
        thread.Join();
    } 

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);

    Thread.Sleep(200);
    Console.WriteLine(x);
}

void Go()
{
    for(int i=0;i<10000;i++)
    {
        x++;
        Thread.Sleep(0);
    }
}

解决方案

您不再需要使用Sleep(1)而不是Sleep(0),因为Microsoft更改了Windows API Sleep()的实现.

来自 MSDN文档对于Sleep(),这就是Sleep(0)现在发生的情况:

值为零会导致线程将其时间片的其余部分放弃给任何其他可以运行的线程.如果没有其他线程可以运行,该函数将立即返回,并继续执行该线程.

这是Windows XP中曾经发生的事情:

值为零会导致线程将其时间片的其余部分放弃给任何其他具有相同优先级的线程,这些线程已准备好运行.如果没有其他具有相同优先级的线程可以运行,则该函数立即返回,并且该线程继续执行.从Windows Server 2003开始,此行为已更改.

请注意任何其他线程"和具有相同优先级的任何其他线程"之间的区别.

Joe Duffy建议使用Sleep(1)而不是Sleep(0)的唯一原因是因为它是最短的Sleep()值,如果没有其他线程,它将阻止Sleep()立即返回在Windows XP上运行时,具有相同优先级的已准备好运行.

对于Windows Server 2003之后的OS版本,您无需担心此问题,因为Sleep()的行为已发生变化.

我提请您注意Joe博客的这一部分:

即使其中有一个明确的Sleep,发出它也不允许安排生产者,因为它的优先级较低.

在XP中,即使主线程(具有较高优先级)执行了Sleep(0),低优先级的线程也会被饿死.在XP之后,由于Sleep(0)将允许较低优先级的线程运行,因此将不再发生这种情况.

Joe Duffy (author of Concurrent Programming on Windows) writes in this blog article that Thread.Sleep(1) is preferred over Thread.Sleep(0) because it will suspend for same and lower priority threads, not just equal priority threads as for Thread.Sleep(0).

The .NET version of MSDN says that Thread.Sleep(0) is special, it will suspend this thread and allow other waiting threads to execute. But it says nothing about Thread.Sleep(1) (for any .NET version).

So, is Thread.Sleep(1) actually doing anything special?

Background:

I'm refreshing my knowledge of concurrent programming. I wrote some C# code to visibly show that pre/post increments and decrements are non-atomic and therefore not thread-safe.

To avoid needing to create hundreds of threads I place a Thread.Sleep(0) after incrementing a shared variable to force the scheduler to run another thread. This regular swapping of threads makes the non-atomic nature of pre/post increment/decrement more obvious.

Thread.Sleep(0) appears to causes no additional delay, as expected. However if I change this to Thread.Sleep(1), it appears to revert to normal sleep behaviour (eg. I get roughly a minimum of 1ms delay).

This would mean that while Thread.Sleep(1) may be preferred, any code that uses it in a loop would run much slower.

This SO question "Could someone explain this interesting behaviour with Sleep(1)?" is sort of relevant, but it is C++ focused and just repeats the guidance in Joe Duffy's blog article.

Here's my code for anyone interested (copied from LinqPad, so you may need to add a class around it):

int x = 0;

void Main()
{
    List<Thread> threadList=new List<Thread>();
    Stopwatch sw=new Stopwatch();

    for(int i=0; i<20; i++)
    {
        threadList.Add(new Thread(Go)); 
        threadList[i].Priority=ThreadPriority.Lowest;
    }

    sw.Start();

    foreach (Thread thread in threadList)
    {
        thread.Start();
    } 


    foreach (Thread thread in threadList)
    {
        thread.Join();
    } 

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);

    Thread.Sleep(200);
    Console.WriteLine(x);
}

void Go()
{
    for(int i=0;i<10000;i++)
    {
        x++;
        Thread.Sleep(0);
    }
}

解决方案

You no longer need to use Sleep(1) instead of Sleep(0) because Microsoft changed the implementation of the Windows API Sleep().

From the MSDN documentation for Sleep(), this is what happens now with Sleep(0):

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.

This is what used to happen in Windows XP:

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution. This behavior changed starting with Windows Server 2003.

Note the difference between "any other thread" and "any other thread of equal priority".

The only reason that Joe Duffy suggests using Sleep(1) rather than Sleep(0) is because it is the shortest Sleep() value that will prevent the Sleep() from returning immediately if there are no other threads of equal priority ready to run, when running on Windows XP.

You don't need to worry about this for OS versions after Windows Server 2003 because of the change in behaviour of Sleep().

I draw your attention to this part of Joe's blog:

And even though there's an explicit Sleep in there, issuing it doesn't allow the producer to be scheduled because it's at a lower priority.

In XP, lower priority threads would be starved even if the main thread (of higher priority) did Sleep(0). Post-XP, this will no longer happen because Sleep(0) will allow the lower priority threads to run.

这篇关于Thread.Sleep(1)是否特殊?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆