.net - C#스레드는 잠자기하지 않습니까?




multithreading (6)

나는이 코드를 가지고 :

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

간격이 1000ms이고 작업 프로세스가 3000ms 인 경우 어떻게되는지보고 싶었습니다.

그러나 나는 이상한 행동을 보았습니다 - 3000ms 지연 은 시작시에만 발생합니다!

어떻게 각각의 doWork 시간 동안 잠을 자도록 할 수 있습니까?

여기에서 볼 수 있듯이, 처음에는 3 초의 지연이 있고, 각각 1 초씩 반복됩니다.


Answers

매 초마다 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 초 동안 휴면 상태이지만 1 초마다 덤프가 발생합니다.

스레드와 어떻게 작동합니까? 이 같은 smth :

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

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

귀하의 예는 매우 흥미 롭습니다 - 병렬 처리의 부작용을 보여줍니다. 귀하의 질문에 대답하고 부작용을보다 쉽게 ​​볼 수 있도록하기 위해 예제를 약간 수정했습니다.

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

여기서 수정 한 내용은 다음과 같습니다.

  • 타이머 간격은 이제 10ms이고 스레드는 여전히 3000ms입니다. 스레드가 잠자는 동안 새로운 스레드가 생성됩니다.
  • 현재 활성 상태 인 스레드 수를 세는 varialbe t 추가했습니다 (스레드가 시작될 때 증가하고 스레드가 종료되기 직전에 감소됨)
  • 스레드 시작과 스레드 끝을 인쇄하는 2 개의 덤프 문을 추가했습니다.
  • 마지막으로, 함수 doWork 의 매개 변수에 기본 변수 h의 값을 볼 수있는 다른 이름 (h2)을 지정했습니다.

이제는 LinqPad 에서 수정 된 프로그램의 출력을 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]

나는 그 값들이 스스로를 생각한다고 생각한다 : 일어나고있는 것은 10ms마다 새로운 스레드가 시작되고, 다른 스레드는 여전히 잠자고있는 것이다. 또한 재미있는 것은 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; }} 
      }

이는 더 깔끔한 접근법이지만이 예제에 표시된 효과는 변경하지 않았습니다.

이제, 각 스레드가 실제로 3000ms (= 3s)를 잠자고 있음을 증명하기 위해 작업자 스레드 doWork Stopwatch 를 추가합시다.

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

스레드를 적절히 정리하려면 ReadLine 다음에 타이머를 사용하지 않도록 설정하십시오.

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

이렇게하면 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] 

예상대로 그들은 모두 하나씩 종료되는 것을 볼 수 있으며 약 3 초 (또는 3000ms) 잤다.


매 초마다 새 스레드를 실행하고있는 것처럼 보이지 않는데, backgroundworker를 사용하고 이벤트 backgroundworker가 C 함수를 다시 호출하면 타이머가 필요 없습니다.


각 doWork는 3 초 동안 휴면 상태이지만 1 초 간격으로 스레드를 작성하므로 휴면 상태가 겹칩니다.


타이머가 틱 할 때마다 잠을 자도록 스레드를 시작합니다. 그 스레드는 완전히 격리되어 있고, 타이머는 매초마다 계속 발사 할 것입니다. 사실, Sleep(3000)c() 로 옮기 더라도 타이머는 1 초마다 작동 합니다.

당신은 현재 무엇입니까 :

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() 의 목적이 여기에 있는지 확실하지 않습니다. 또 다른 옵션은 Sleep() 있는 while 루프입니다. 단순하고 많은 스레드를 필요로하지 않습니다.


synchronized 블록에서 호출해야합니다 : wait() 메서드는 항상 동기화 된 블록에서 호출됩니다. 즉 wait()메서드는 호출 된 객체보다 먼저 객체 모니터를 잠글 필요가 있습니다. 그러나 sleep()메소드는 외부 동기화 된 블록에서 호출 할 수 있습니다. 즉, sleep()메소드에는 객체 모니터가 필요하지 않습니다.

IllegalMonitorStateException :wait() 메소드가 IllegalMonitorStateException실행시에 throw되는 것보다 객체 락을 취득하지 않고 불려 갔을 경우, 메소드는 sleep()그러한 예외를 throw하지 않습니다.

class : wait() 메소드가 java.lang.Object클래스에 sleep()속 하지만 메소드가 java.lang.Thread클래스에 속하는 것 .

오브젝트 또는 스레드에서 호출 : wait() 메소드는 오브젝트에서 호출되지만 sleep()메소드는 오브젝트가 아닌 스레드에서 호출됩니다.

스레드 상태 :wait()메소드가 객체에서 호출, 즉 만들었 었지 객체의 모니터는 대기 상태로 실행 가고 경우에만 실행 가능한 상태로 돌아갈 수 있습니다 스레드 notify()또는 notifyAll()객체에서 호출하는 방법. 나중에 스레드 스케줄러는 스레드를 실행 가능 상태에서 실행 상태로 이동하도록 예약합니다. sleep()쓰래드에서 호출 될 때 그것은 실행 상태에서 대기 상태로 돌아가고, 수면 시간이되면 실행 가능한 상태로 되돌아 갈 수 있습니다.

synchronized 블록에서 호출 될 때 :wait() 메서드가 호출 될 때 스레드는 객체 잠금 상태를 유지합니다. 그러나 sleep()동기화 된 블록 또는 메서드 스레드에서 호출 될 때 메서드가 개체 잠금을 남기지 않습니다.

추가 Reference





c# .net multithreading