54

我有这个代码:

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();
}

我想看看如果间隔是 1000 毫秒并且作业进程是 3000 毫秒会发生什么。

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

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

正如您在此处看到的,一开始有 3 秒的延迟,然后每次迭代 1 秒。

在此处输入图像描述

4

6 回答 6

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)
...

目前尚不清楚您要做什么。您可以在不希望它触发时禁用计时器,并在准备好后再次恢复它,但尚不清楚Sleep()此处的目的是什么。另一种选择只是一个while带有 a 的循环Sleep()。简单,并且不涉及很多线程。

于 2012-07-03T09:18:51.483 回答
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);
}
于 2012-07-03T09:21:21.980 回答
9

Your example is very interesting - it shows the side effects of parallel processing. To answer your question, and to make it easier to see the side effects, I've slightly modified your example:

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);
        }
    }
}

What I've modified here is the following:

  • Timer interval is now 10 ms, the threads still have 3000 ms. The effect is that while threads are sleeping, new threads will be created
  • I've added varialbe t, which counts the number of threads currently being active (it is increased when the thread starts and decreased right before the thread ends)
  • I've added 2 dump statements, printing out the thread start and the thread end
  • Finally, I've given the parameter of function doWork a different name (h2), which allows to see the value of the underlying variable h

Now it is intersting to see the output of this modified program in LinqPad (note the values are not always the same as they're depending on the race conditions of the started threads):

    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]

I think the values speak for themselves: What is happening is that every 10 ms a new thread is started, while others are still sleeping. Also interesting is to see that h is not always equal to h2, especially not if more threads are started while others are sleeping. The number of threads (variable t) is after a while stabilizing, i.e. running around 190-194.

You might argue, that we need to put locks on the variables t and h, for example

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; }} 
      }

While that is a cleaner approach, it didn't change the effect shown in this example.

Now, in order to prove that each thread really sleeps 3000ms (= 3s), let's add a Stopwatch to the worker thread 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();
    }
} 

For a proper cleanup of the threads, let's disable the timer after the ReadLine as follows:

    Console.ReadLine(); t.Enabled=false; 

This allows you to see what happens if no more threads are starting, after you've pressed 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] 

You can see they are all being terminated one after the other as expected and they slept about 3s (or 3000ms).

于 2012-07-03T11:46:32.937 回答
6

您看到这种行为的原因很简单:您每秒安排一个新线程,结果在三秒后变得可见。前四秒你什么都看不到;然后,三秒前启动的线程转储;到那时,另一个线程将已经休眠两秒钟,而另一个线程将休眠一秒钟。下一秒线程#2 转储;然后线程#3、#4 等等——你每秒都会得到一个打印输出。

如果您希望每三秒看到一次打印输出,您应该每三秒安排一个新线程,并带有您想要的任何延迟:初始线程将在三秒加上延迟后输出;所有后续线程将以三秒的间隔触发。

于 2012-07-03T09:20:40.387 回答
2

似乎你每秒都在运行一个新线程,这不是一个好主意,使用 backgroundworker,当事件 backgroundworker 完成时再次调用 C 函数,这样你就不需要计时器

于 2012-07-03T09:15:11.380 回答
2

每个 doWork 都在休眠三秒钟,但它们的休眠重叠,因为您以一秒钟的间隔创建线程。

于 2012-07-03T09:19:10.980 回答