2010-12-08 15 views
0

"連絡先"ごとにスレッドを開始し、その連絡先の結果を(ネットワーク経由で)照会する関数を作成しようとしています。私は待機関数が応答のために最大1.5秒待つことを望み、その後は残りのスレッドを終了させるだけです。C#スレッド競合状態

私が抱えている問題は、論理によれば、このことは不可能ではあるが、すべてのスレッドが完了する前に関数が戻ってくることです。

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ラインは、Get(によって返されたどのように多くの結果を見て、メインテストプログラムから来ている:すべてのスレッドが完全に完了している、まだ私は次のような出力が得られるまで、メイン機能でwhileループを待っている必要があります))

私が見ることによると、これは可能ではありません。競合状態が発生している可能性がある(または、私が行った他の前提は正しくない)誰かが知っていますか?

関数の定義は次のようなある:あなたが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を組み合わせると、コードを大幅に簡素化できます。基本的に、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()); 

を代わりに手動でやっていたようにスピンロックを管理する、.NETはあなたのためにそれをやらせます。