2012-07-03 10 views
54

tengo este código:C# ¿El subproceso no duerme?

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

quería ver lo que sucede si el intervalo es de 1000 ms y el proceso de trabajo es 3000 ms.

Sin embargo, vi un comportamiento extraño - el retraso de 3000 ms se produce solo al comienzo!

¿Cómo puedo hacer que cada doWork duerma 3000 ms?

Como puede ver aquí, al principio hay un retraso de 3 segundos, y luego itera 1 segundo cada uno.

enter image description here

+5

No veo el problema. Lo que sucede es exactamente lo que esperas de un escenario de subprocesos múltiples. – leppie

+0

Cuidado: *** ¡Es una trampa! *** Lo que ves no tiene mucho que ver con lo que querías lograr, creo. –

+0

No veo ningún problema aquí, cuando generas hilos con tres segundos de sueño en cada uno, pero el desove ocurre una vez por segundo, entonces tienes un retraso inicial ya que el primero tiene que "esperar" los tres segundos para pasar, pero todos los demás lo siguen con un desplazamiento de un segundo. – Gorgsenegger

Respuesta

65

Cada vez que el temporizador marca, comienza un hilo para dormir un poco; ese hilo está completamente aislado, y el temporizador va a seguir disparando cada segundo. En realidad, el temporizador dispara cada segundo incluso si mueve el Sleep(3000) al c().

Lo que tenemos actualmente es:

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

No está claro lo que está tratando de hacer. Puede desactivar el temporizador cuando no desee que se dispare y reanudarlo una vez que esté listo, pero no está claro cuál es el propósito del Sleep() aquí. Otra opción es simplemente un bucle while con un Sleep() en él. Simple, y no implica muchos hilos.

+2

¡Gracias por explicar de verdad lo que está sucediendo! – aukaost

+0

@MarcGravell gracias (sé que el uso de subprocesos es costoso ... sigue siendo solo para aprender.) –

+6

@RoyiNamir si esto es para aprender, entonces por favor aprenda que esta es una forma realmente costosa de hacer un trabajo programado; p –

2

parece que RUNING un nuevo hilo cada segundo wich no es una buena idea, el uso BackgroundWorker, y cuando el BackgroundWorker evento completado llamar a la función C de nuevo, de esa manera usted no necesita un temporizador

+2

No quiero usar backgroundworker. Quiero usar comandos de hilo simple ... :) –

+0

Luego, simplemente elimine el código del temporizador, y haga un estilo/patrón recursivo cuando el hilo esté terminado, duerma 1 segundo y vuelva a llamar a la función/método C – JohnnBlade

+2

"No quiero usar la herramienta correcta, quiero usar la incorrecta "no es muy útil para una solución. –

2

Cada doWork duerme durante tres segundos, pero su sueño se solapa porque crea los hilos en intervalos de un segundo.

6

La razón por la que ve este comportamiento es simple: programa un nuevo hilo cada segundo, y el resultado se vuelve visible tres segundos después. No ves nada durante los primeros cuatro segundos; luego, el hilo que se inició hace tres segundos vuelca; otro hilo habrá estado durmiendo durante dos segundos para entonces, y otro más, por un segundo. El segundo segundo hilo # 2 vuelca; luego, rosca # 3, # 4, y así sucesivamente - usted obtiene una impresión por segundo.

Si desea ver una impresión cada tres segundos, debe programar una nueva secuencia cada tres segundos con cualquier retraso que desee: la secuencia inicial se emitirá en tres segundos más la demora; todos los subprocesos posteriores se activarán en intervalos de tres segundos.

15

Cada segundo que inicia un nuevo hilo con 3 segundos de retraso.Sucede así:

  1. hilo 1 de inicio
  2. hilo 2 de inicio, el hilo 1 tiene capacidad
  3. hilo 3 de inicio, hilo 2 Camas, hilo 1 plazas
  4. hilo 4 de inicio, hilo 3 Camas, hilo 2 Camas, hilo 1 duerme
  5. hilo 5 de inicio, rosca 4 Camas, hilo 3 Camas, hilo 2 Camas, hilo 1 vertederos
  6. comienzo de la rosca 6, hilo 5 Camas, rosca 4 Camas, hilo 3 Camas, hilo 2 vertederos
  7. hilo 7 inicio, hilo para 6 personas, 5 plazas de hilo, hilo para 4 personas, 3 hilo vertederos

Como se puede ver, cada hilo tiene capacidad para 3 segundos, sin embargo, se produce un volcado cada segundo.

¿Cómo funciona uno con los hilos? smth así:

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

public void doWork() 
{ 
    int h = 0; 
    do 
    { 
     Thread.Sleep(3000); 
     h.Dump(); 
     h++; 
    }while(true); 
} 
9

Su ejemplo es muy interesante: muestra los efectos secundarios del procesamiento en paralelo. Para responder a su pregunta, y para que sea más fácil ver los efectos secundarios, he modificado ligeramente su ejemplo:

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

Lo he modificado aquí es la siguiente:

  • intervalo del temporizador es ahora 10 ms, los hilos todavía tienen 3000 ms. El efecto es que mientras los hilos están durmiendo, se crearán nuevos hilos
  • He añadido varialbe t, que cuenta el número de hilos actualmente activos (se incrementa cuando el hilo comienza y disminuye justo antes de que termine el hilo)
  • he añadido 2 declaraciones de volteo, imprimiendo el comienzo de la rosca y el extremo del hilo
  • Por último, he dado el parámetro de la función doWork un nombre diferente (h2), lo que permite ver el valor de la variable subyacente h

Ahora es intersting ver la salida de este programa modificado en LinqPad (tenga en cuenta los valores no siempre son los mismos que están en función de las condiciones de la carrera de las discusiones iniciadas):

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] 

Creo que los valores hablan por sí mismos: Lo que está sucediendo es que cada 10 ms un nuevo el hilo se inicia, mientras que otros todavía están durmiendo. También es interesante ver que h no siempre es igual a h2, especialmente no si se inician más hilos mientras que otros están durmiendo. El número de subprocesos (variable t) se estabiliza después de un tiempo, es decir, se ejecuta alrededor de 190-194.

Se podría argumentar, que tenemos que poner cerraduras en las variables T y H, por ejemplo

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

Mientras que es un enfoque más limpio, que no cambió el efecto se muestra en este ejemplo.

Ahora, con el fin de demostrar que cada hilo realmente duerme 3000 ms (= 3s), vamos a añadir un Stopwatch para el subproceso de trabajo 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(); 
    } 
} 

Para una limpieza adecuada de los hilos, vamos a desactivar el temporizador después de la ReadLine de la siguiente manera:

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

Esto le permite ver lo que sucede si no hay más hilos están empezando, después de pulsar 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] 

Puede ver que todas terminan una tras otra como se esperaba y durmieron aproximadamente 3s (o 3000ms).

Cuestiones relacionadas