0

내 응용 프로그램에서 다중 스레드를 사용하려고합니다. 메서드 test5은 인터넷에서 일부 콘텐츠를 가져 오려고 시도하지만 main 스레드는 다른 스레드를 계속하기 전에 모든 스레드가 완료 될 때까지 대기합니다.하위 스레드가 완료되면 주 스레드가 진행되지 않습니다.

하지만 내 main 스레드는 test5을 호출 한 후에 다시 돌아 오지 않으며 내 콘솔 라인 Done Inside!!thread all got back!!에는 결코 도달하지 않습니다.

이 문제를 어떻게 해결할 수 있습니까? 이것

class Program 
{ 
    static void Main(string[] args) 
    { 
     string[] url = 
     { 
      "http://...", "http://...", "http://...", "http://...", "http://..." 
     }; 

     test5(url); 
     Console.WriteLine("thread all got back!!"); 

     // Do some other work after all threads come back 
     Console.ReadLine(); 
    } 

    private static void test5(string[] _url) 
    { 
     int numThreads = _url.Length; 
     ManualResetEvent resetEvent = new ManualResetEvent(false); 
     int toProcess = numThreads; 

     for (int i = 0; i < numThreads - 1; i++) 
     { 
      new Thread(delegate() { 
       testWebWorking(_url[i]); 
       if (Interlocked.Decrement(ref toProcess) == 0) 
        resetEvent.Set(); 
      }).Start(); 
     } 
     resetEvent.WaitOne(); 
     Console.WriteLine("Done inside!!"); 
    } 

    private static void test6(string[] _url) 
    { 
     int numThreads = _url.Length; 
     var countdownEvent = new CountdownEvent(numThreads); 

     for (int i = 0; i < numThreads - 1; i++) 
     { 
      new Thread(delegate() { 
       testWebWorking(_url[i]); 
       countdownEvent.Signal(); 
      }).Start(); 
     } 
     countdownEvent.Wait(); 
     Console.WriteLine("Done inside!!"); 
    } 

    private static void testWebWorking(object url) 
    { 
     Console.WriteLine("start {0}", Thread.CurrentThread.ManagedThreadId); 
     string uri = (string)url; 
     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 
     request.KeepAlive = true; 
     request.Timeout = 5000; 
     request.ReadWriteTimeout = 5000; 
     request.Proxy = null; 

     try 
     { 
      using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 
      { 
       //Console.WriteLine(response.ContentType + "; uri = " + uri); 
       Stream receiveStream = response.GetResponseStream(); 
       Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); 
       // Pipes the stream to a higher level stream reader with the required encoding format. 
       StreamReader readStream = new StreamReader(receiveStream, encode); 
       //Console.WriteLine("\r\nResponse stream received."); 
       Char[] read = new Char[256]; 
       // Reads 256 characters at a time.  
       int count = readStream.Read(read, 0, 256); 
       //Console.WriteLine("HTML...\r\n"); 
       String str = ""; 
       while (count > 0) 
       { 
        // Dumps the 256 characters on a string and displays the string to the console. 
        str = new String(read, 0, count); 
        //Console.Write(str); 
        count = readStream.Read(read, 0, 256); 
       } 
       //Console.WriteLine(str); 
       // Releases the resources of the response. 
       response.Close(); 
       // Releases the resources of the Stream. 
       readStream.Close(); 

       Console.WriteLine("end {0}", Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
     catch (WebException ex) 
     { 
      //Console.WriteLine(ex.GetBaseException().ToString()); 
      //Console.WriteLine(url); 
      Console.WriteLine("time out !!"); 
     } 
     finally 
     { 
      request.Abort(); 
      request = null; 
      GC.Collect(); 
     } 
    } 
} 

답변

2

봐 :

for (int i = 0; i < numThreads - 1; i++) 

는 만 numThreads - 1 스레드를 시작하고 있습니다. 귀하의 카운터는이 또한 고장 ::

for (int i = 0; i < numThreads - 1; i++) 
{ 
    new Thread(delegate() 
    { 
     testWebWorking(_url[i]); 
     ... 
    } 
    ... 
} 

를 여기, 당신은 를 캡처하고, numThreads에서 시작 카운트 다운, 그래서 오직 하나, 여담으로 0이 아닌

에 도달 할 것 대리자 내에서 변수i을 사용하므로 실행시 값이 i이됩니다. 같은 URL을 여러 번 테스트하고 다른 URL을 건너 뛸 수 있습니다. 다른 변수마다 캡처 있도록 대신, 변수 내에서 루프에 i의 값을 복사한다 : 당신의 방법

// Fixed the loop boundary as well 
for (int i = 0; i < numThreads; i++) 
{ 
    int copy = i; 
    new Thread(() => // Use a lambda expression for brevity 
    { 
     // Fix naming convention for method name, too... 
     TestWebWorking(_url[copy]); 
     if (Interlocked.Decrement(ref toProcess) == 0)) 
     { 
      resetEvent.Set(); 
     } 
    }).Start() 
} 

모두를 문제의 동일한 세트가 있습니다.

개인적으로 어차피 여기 for 루프를 사용하지 것이다 - 나는 foreach 루프를 사용하십시오 : 또는

private static void TestWithResetEvent(string[] urls) 
{ 
    ManualResetEvent resetEvent = new ManualResetEvent(false); 
    int counter = urls.Length; 

    foreach (string url in urls) 
    { 
     string copy = url; 
     Thread t = new Thread(() => 
     { 
      TestWebWorking(copy); 
      if (Interlocked.Decrement(ref toProcess) == 0)) 
      { 
       resetEvent.Set(); 
      } 
     }); 
     t.Start(); 
    } 
    resetEvent.WaitOne(); 
    Console.WriteLine("Done inside!!"); 
} 

를, 그것은 간단하게 될 것입니다 것은 정확히 이런 종류의 위해 설계 Parallel.ForEach를 사용하는 .

+0

감사합니다. 잘 작동합니다. 그것은 내 문제의 데모에 불과하므로 ** for 루프 **를 사용하여 보여줍니다. 실제 코드에서는 ** while 루프 **를 여러 개 사용합니다. 실제 상황은 좀 더 복잡합니다. –

+0

내 실제 코드, ** numThreads ** 알 수없고 여러 while 루프를 서로 있습니다. ** numThreads **는 모든 루프가 완료 될 때까지 알지 못합니다 ... 어떻게 해결합니까? 루프가 끝나면 모든 스레드를 실행해야합니까? 또는 위와 같이 루프 내에서 스레드를 실행할 수 있습니다. –

관련 문제