2010-12-08 3 views
0

각 "연락처"에 대한 스레드를 시작한 다음 해당 연락처의 결과에 대해 네트워크를 통해 쿼리하는 함수를 작성하려고합니다. 기다리는 함수가 응답을 위해 최대 1.5 초 동안 기다린 후, 나머지 스레드를 종료하면됩니다.C# 스레딩 경쟁 조건

내가 겪고있는 문제는 모든 스레드가 완료되기 전에 함수가 반환된다는 것입니다. 논리에 따르면 이것이 가능하지 않아도됩니다. 모든 스레드가 완전히 완료 될 때까지 주요 함수의 while 루프는 대기해야한다, 그러나 나는 다음과 같은 출력을 얻을 : (

FAIL: Storage test 1 exists 0 times in the DHT. 
    : Storage test 2 exists 0 times in the DHT. 
Added storage test 1 to the entries. 
Added storage test 2 to the entries. 

합니다 (FAIL 라인 가져 오기에 의해 반환 얼마나 많은 결과를보고 주요 테스트 프로그램에서 온다).

내가 볼 수있는 바에 따르면, 이것은 가능하지 않아야한다. 누구든지 경쟁 조건이 발생할 수있는 위치를 알고 있습니까 (또는 다른 가정이 맞지 않습니다)?

함수 정의

같은 것입니다 : 당신이 결코 List<Thread> (스레드)에 Thread (t)를 넣어하지있는 것처럼

public IList<Entry> Get(ID key) 
    { 
     ConcurrentBag<Entry> entries = new ConcurrentBag<Entry>(); 
     List<Thread> threads = new List<Thread>(); 
     foreach (Contact c in this.p_Contacts) 
     { 
      Thread t = new Thread(delegate() 
      { 
       try 
       { 
        FetchMessage fm = new FetchMessage(this, c, key); 
        fm.Send(); 
        int ticks = 0; 

        // Wait until we receive data, or timeout. 
        while (!fm.Received && ticks < 1500) 
        { 
         Thread.Sleep(100); 
         ticks += 100; 
        } 

        if (fm.Received) 
        { 
         foreach (Entry e in fm.Values) 
         { 
          Console.WriteLine("Added " + e.Value + " to the entries."); 
          entries.Add(e); 
         } 

         if (entries.Count == 0) 
          Console.WriteLine("There were no entries to add."); 
        } 
        else 
         Console.WriteLine("The node did not return in time."); 
       } 
       catch (Exception e) 
       { 
        Console.WriteLine(e); 
       } 
      } 
      ); 
      t.IsBackground = false; 
      t.Start(); 
     } 

     while (true) 
     { 
      bool stopped = true; 
      foreach (Thread t in threads) 
      { 
       if (t.ThreadState != ThreadState.Stopped) 
        stopped = false; 
      } 
      if (stopped) 
       break; 
      Thread.Sleep(100); 
     } 

     return new List<Entry>(entries.ToArray()); 
    } 
+1

while (true)주기를 'foreach (스레드 t 스레드) t.Join()'으로 대체 한 다음 Toby의 조언에 따라 'threads.Add (t)'를 't.Start) '[참고 : 내 키보드에 역 인용 키가 없으므로 누군가 내 의견을 고칠 수 있습니까?] –

답변

5

보인다. foreach 루프가 실행되지 않습니다.

주 스레드는 단지 100ms를 기다리고 계속됩니다.

+0

Toby가 말한 내용은 다음과 같습니다. – Anton

+0

Heh는 그 점을 발견하지 못했습니다. 때로는 가장 간단한 문제 일 수 있습니다. P –

0

스레드가 목록에 추가되지 않아 while 루프가 즉시 중단됩니다.

0

이 문제에 대한 해결책은 자신의 스레드가 완료 한 연락처를 추적 유지하는 ConcurrentDictionary을 사용하는 것이었다 :

public IList<Entry> Get(ID key) 
    { 
     ConcurrentBag<Entry> entries = new ConcurrentBag<Entry>(); 
     ConcurrentDictionary<Contact, bool> done = new ConcurrentDictionary<Contact, bool>(); 
     List<Thread> threads = new List<Thread>(); 
     foreach (Contact c in this.p_Contacts) 
     { 
      Thread t; 
      ThreadStart ts = delegate() 
      { 
       try 
       { 
        FetchMessage fm = new FetchMessage(this, c, key); 
        fm.Send(); 
        int ticks = 0; 

        // Wait until we receive data, or timeout. 
        while (!fm.Received && ticks < 1500) 
        { 
         Thread.Sleep(100); 
         ticks += 100; 
        } 

        if (fm.Received) 
        { 
         foreach (Entry e in fm.Values) 
         { 
          Console.WriteLine("Added " + e.Value + " to the entries."); 
          entries.Add(e); 
         } 

         if (entries.Count == 0) 
          Console.WriteLine("There were no entries to add."); 
        } 
        else 
         Console.WriteLine("The node did not return in time."); 

        Thread.MemoryBarrier(); 
        done[c] = true; 
       } 
       catch (Exception e) 
       { 
        Console.WriteLine(e); 
       } 
      }; 
      t = new Thread(ts); 
      done[c] = false; 
      t.IsBackground = true; 
      t.Start(); 
     } 

     while (true) 
     { 
      bool stopped = true; 
      foreach (Contact c in this.p_Contacts) 
      { 
       if (!done[c]) 
        stopped = false; 
      } 
      if (stopped) 
       break; 
      Thread.Sleep(100); 
     } 

     return new List<Entry>(entries.ToArray()); 
    } 
2

@Toby 정답을 가지고,하지만 내가 다른 일을 소개 할 수있는 경우 개선 코드. 기본적으로 자신의 ThreadPool과 시간 초과를 수동으로 관리하고 있습니다. 이것은 .NET에서 제공하는 것입니다. 참조 : http://msdn.microsoft.com/en-us/library/system.threading.threadpool(v=VS.100).aspx

ThreadPool을 .Net 4 배리어와 결합하면 코드를 훨씬 단순하게 만들 수 있습니다. 본질적으로 Barrier는 모든 스레드가 동기화 될 때까지 차단합니다. 스레드와 동일한 장벽을 통과하고 끝에 동기화 할 때 모든 작업자 스레드가 완료 될 때까지 주 스레드를 일시 중지 할 수 있습니다. 리팩토링 코드는 다음과 같습니다

// For the number of threads + 1 for the main thread 
Barrier barrier = new Barrier(this.p_Contacts.count + 1); 
ConcurrentBag<Entry> entries = new ConcurrentBag<Entry>(); 

foreach (Contact c in this.p_Contacts) 
{ 
    ThreadPool.RegisterWaitForSingleObject(
     new EventWaitHandle(false, EventResetMode.AutoReset), 
     (stateInfo,timedOut) => { 
      try 
      { 
       FetchMessage fm = new FetchMessage(this, c, key); 
       fm.Send(); 

       while(!fm.Received || !timedOut) 
       { 
        Thread.Sleep(100); 
       } 

       if(fm.Received) 
       { 
        foreach (Entry e in fm.Values) 
        { 
         entries.Add(e); 
         Console.WriteLine("Added " + e.Value + " to the entries."); 
        } 

        // avoid counting other thread's work 
        if (fm.Values.count == 0) 
        { 
         Console.WriteLine("There were no entries to add."); 
        } 
       } 
       else 
       { 
        Console.WriteLine("The node did not return in time."); 
       } 

       barrier.SignalAndWait(); 
      } 
      catch(Exception e) 
      { 
       Console.WriteLine(e); 
      } 
     }, null, TimeSpan.FromSeconds(1.5), true); 
    ); 
} 

// This limits total time waited to only 1.5 seconds 
barrier.SignalAndWait(TimeSpan.FromSeconds(1.5)); 

return new List<Entry>(entries.ToArray()); 

을 대신 수동으로하고있는 것처럼 스핀 잠금을 관리, 닷넷은 당신을 위해 그것을 할 수 있습니다.