Thread.Sleep(1)特别吗?

时间:2013-05-16 09:58:06

标签: c# multithreading

Joe Duffy(Concurrent Programming on Windows的作者)在this blog article中写道,Thread.Sleep(1)比Thread.Sleep(0)更受欢迎,因为它将暂停相同和较低优先级的线程,而不仅仅是与Thread.Sleep(0)相同的优先级线程。

Thread.Sleep(0)的.NET version of MSDN says是特殊的,它将挂起该线程并允许其他等待线程执行。但它没有提及Thread.Sleep(1)(对于任何.NET版本)。

那么,Thread.Sleep(1)实际上做了什么特别的事情吗?

背景:

我正在刷新我对并发编程的了解。我写了一些C#代码,可以明显地显示前/后增量和减量是非原子的,因此不是线程安全的。

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

如预期的那样,Thread.Sleep(0)似乎不会导致额外的延迟。但是,如果我将其更改为Thread.Sleep(1),它似乎恢复到正常的睡眠行为(例如,我大约至少延迟1ms)。

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

这个问题"Could someone explain this interesting behaviour with 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);
    }
}

1 个答案:

答案 0 :(得分:58)

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

the MSDN documentation for Sleep()开始,这就是Sleep(0)现在发生的事情:

  

值为零会导致线程将其时间片的剩余部分放弃到准备运行的任何其他线程。如果没有其他线程准备好运行,则该函数立即返回,并且线程继续执行。

这是以前在Windows XP中发生的事情:

  

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

请注意“任何其他线程”与“任何其他具有相同优先级的线程”之间的区别。

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

由于Sleep()的行为发生了变化,因此Windows Server 2003之后的操作系统版本无需担心这一点。

我提醒你注意Joe博客的这一部分:

  

即使在那里有明确的Sleep,发布它也不允许生产者被安排,因为它的优先级较低。

在XP中,即使主线程(优先级较高)执行Sleep(0),优先级较低的线程也会被饿死。在XP之后,这将不再发生,因为Sleep(0)将允许较低优先级的线程运行。