C#线程不会睡觉?

Roy*_*mir 54 .net c# multithreading

我有这个代码:

void Main()
{
    System.Timers.Timer t = new System.Timers.Timer (1000);
    t.Enabled=true;
    t.Elapsed+= (sender, args) =>c();
    Console.ReadLine();

}

int h=0;
public void c()
{
    h++;
    new Thread(() => doWork(h)).Start();
}

public void doWork(int h)
{
    Thread.Sleep(3000);
    h.Dump();
}
Run Code Online (Sandbox Code Playgroud)

我想看看如果间隔为1000毫秒且作业过程为3000毫秒会发生什么.

但是我看到了一个奇怪的行为 - 3000毫秒延迟仅在开始时发生!

如何让每次doWork睡眠时间为3000毫秒?

正如你在这里看到的那样,开始时有3秒的延迟,然后每次迭代1秒.

在此输入图像描述

Mar*_*ell 65

每次计时器滴答时,你都会开始一个线程来做一些睡眠; 该线程完全隔离,计时器将继续每秒触发.实际上,即使Sleep(3000)进入,计时器也会每秒触发一次c().

你现在拥有的是:

1000 tick (start thread A)
2000 tick (start thread B)
3000 tick (start thread C)
4000 tick (start thread D, A prints line)
5000 tick (start thread E, B prints line)
6000 tick (start thread F, C prints line)
7000 tick (start thread G, D prints line)
8000 tick (start thread H, E prints line)
...
Run Code Online (Sandbox Code Playgroud)

目前还不清楚你要做什么.您可以在不希望它开火时禁用定时器,并在准备好后再次恢复它,但目前还不清楚它的用途是什么Sleep().另一种选择只是一个while带有a 的循环Sleep().简单,并且不涉及大量线程.

  • @RoyiNamir如果这是为了学习,那么请知道这是一种非常昂贵的预定工作方式; p (6认同)
  • @MarcGravell谁说我正在做预定的工作?写代码行,看看会发生什么并不是说我正在构建一些东西 (4认同)
  • 感谢您真正解释发生了什么! (2认同)

Age*_*t_L 15

每秒你都会以3秒的延迟开始新的线程.它发生如下:

  1. 线程1开始
  2. 线程2开始,线程1休眠
  3. 线程3开始,线程2休眠,线程1休眠
  4. 线程4开始,线程3休眠,线程2休眠,线程1休眠
  5. 线程5启动,线程4睡眠,线程3睡眠,线程2睡眠,线程1转储
  6. 线程6启动,线程5睡眠,线程4睡眠,线程3睡眠,线程2转储
  7. 线程7启动,线程6睡眠,线程5睡眠,线程4睡眠,线程3转储

如您所见,每个线程都会休眠3秒钟,但每秒都会发生转储.

如何使用线程?像这样:

void Main()
{
    new Thread(() => doWork()).Start();
    Console.ReadLine();
}

public void doWork()
{
    int h = 0;
    do
    {
        Thread.Sleep(3000);
        h.Dump();
        h++;
    }while(true);
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*att 9

您的示例非常有趣 - 它显示了并行处理的副作用.为了回答你的问题,并且为了更容易看到副作用,我稍微修改了你的例子:

using System;
using System.Threading;
using System.Diagnostics;

public class Program
{
    public static void Main()
    {
        (new Example()).Main();
    }
}

public class Example
{
    public void Main()
    {
        System.Timers.Timer t = new System.Timers.Timer(10);
        t.Enabled = true;
        t.Elapsed += (sender, args) => c();
        Console.ReadLine(); t.Enabled = false;
    }

    int t = 0;
    int h = 0;
    public void c()
    {
        h++;
        new Thread(() => doWork(h)).Start();
    }

    public void doWork(int h2)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        try
        {
            t++;
            Console.WriteLine("h={0}, h2={1}, threads={2} [start]", h, h2, t);
            Thread.Sleep(3000);
        }
        finally
        {
            sw.Stop();
            var tim = sw.Elapsed;
            var elapsedMS = tim.Seconds * 1000 + tim.Milliseconds;
            t--;
            Console.WriteLine("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", h, h2, t, elapsedMS);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里修改的内容如下:

  • 定时器间隔现在是10 ms,线程仍然有3000 ms.结果是当线程处于休眠状态时,将创建新线程
  • 我添加了varialbe t,它计算当前处于活动状态的线程数(当线程开始时它会增加,并且在线程结束之前减少)
  • 我添加了2个转储语句,打印出线程开始和线程结束
  • 最后,我给函数的参数doWork一个不同的名称(h2),它允许查看底层变量h的值

现在看看LinqPad中这个修改过的程序的输出是有用的(请注意,这些值并不总是与它们相同,具体取决于已启动线程的竞争条件):

    h=1, h2=1, threads=1 [start]
    h=2, h2=2, threads=2 [start]
    h=3, h2=3, threads=3 [start]
    h=4, h2=4, threads=4 [start]
    h=5, h2=5, threads=5 [start]
    ...
    h=190, h2=190, threads=190 [start]
    h=191, h2=191, threads=191 [start]
    h=192, h2=192, threads=192 [start]
    h=193, h2=193, threads=193 [start]
    h=194, h2=194, threads=194 [start]
    h=194, h2=2, threads=192 [end]
    h=194, h2=1, threads=192 [end]
    h=194, h2=3, threads=191 [end]
    h=195, h2=195, threads=192 [start]
Run Code Online (Sandbox Code Playgroud)

我认为价值观不言自明:正在发生的事情是每10毫秒启动一个新线程,而其他人仍然在睡觉.同样有趣的是看到h并不总是等于h2,尤其是如果在其他人正在睡觉时启动更多线程则不会.线程数(变量t)经过一段时间的稳定,即在190-194左右运行.

你可能会说,我们需要对变量t和h进行锁定,例如

readonly object o1 = new object(); 
int _t=0; 
int t {
       get {int tmp=0; lock(o1) { tmp=_t; } return tmp; } 
       set {lock(o1) { _t=value; }} 
      }
Run Code Online (Sandbox Code Playgroud)

虽然这是一种更简洁的方法,但它并未改变此示例中显示的效果.

现在,为了证明每个线程确实睡眠时间为3000毫秒(= 3秒),让我们Stopwatch在工作线程中添加一个doWork:

public void doWork(int h2) 
{ 
    Stopwatch sw = new Stopwatch(); sw.Start();
    try 
    {
        t++; string.Format("h={0}, h2={1}, threads={2} [start]", 
                            h, h2, t).Dump();                               
        Thread.Sleep(3000);         }
    finally {
        sw.Stop(); var tim = sw.Elapsed;
        var elapsedMS = tim.Seconds*1000+tim.Milliseconds;
        t--; string.Format("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ", 
                            h, h2, t, elapsedMS).Dump();
    }
} 
Run Code Online (Sandbox Code Playgroud)

为了正确清理线程,让我们ReadLine按如下方式禁用定时器:

    Console.ReadLine(); t.Enabled=false; 
Run Code Online (Sandbox Code Playgroud)

这使您可以看到在按下ENTER后没有更多线程启动时会发生什么:

    ...
    h=563, h2=559, threads=5 [end, sleep time=3105 ms] 
    h=563, h2=561, threads=4 [end, sleep time=3073 ms] 
    h=563, h2=558, threads=3 [end, sleep time=3117 ms] 
    h=563, h2=560, threads=2 [end, sleep time=3085 ms] 
    h=563, h2=562, threads=1 [end, sleep time=3054 ms] 
    h=563, h2=563, threads=0 [end, sleep time=3053 ms] 
Run Code Online (Sandbox Code Playgroud)

你可以看到他们都按预期一个接一个地被终止,他们睡了大约3s(或3000ms).


das*_*ght 6

您看到此行为的原因很简单:您每秒安排一个新线程,结果在三秒后可见.前四秒你没有看到任何东西; 然后,三秒钟前启动的线程转储; 到那时另一个线程已经睡了两秒钟,而另一个线程已经睡了两秒钟 - 一秒钟.下一个第二个线程#2转储; 然后线程#3,#4等等 - 你每秒都会得到一个打印输出.

如果你想每三秒钟看一次打印输出,你应该每隔三秒安排一个新的线程,你想要任何延迟:初始线程将在三秒内输出加上延迟; 所有后续线程将以三秒为间隔触发.