2012-07-27 62 views
1

我有一個主要任務是產生線程來完成一些工作。工作完成後,它將寫入控制檯。與序列的多線程

我的問題是稍後創建的某些線程將比先前創建的線程更快完成。不過,我需要寫入控制檯,以與線程創建時相同的順序完成。

所以如果一個線程完成了它的任務,而一些早期的線程沒有完成它,它必須等到那些早期的線程完成。

public class DoRead 
    { 
     public DoRead() 
     { 
     } 

     private void StartReading() 
     { 
      int i = 1; 

      while (i < 10000) 
      { 
       Runner r = new Runner(i, "Work" + i.ToString()); 
       r.StartThread(); 
       i += 1; 
      } 
     } 
    } 

    internal class Runner : System.IDisposable 
    { 
     int _count; 
     string _work = ""; 

     public Runner(int Count, string Work) 
     { 
      _count = Count; 
      _work = Work; 
     } 

     public void StartThread() 
     { 
      ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this); 
     } 

     public static void runThreadInPool(object obj) 
     { 
      ((Runner)obj).run(); 
     } 

     public void run() 
     { 
      try 
      { 
       Random r = new Random(); 
       int num = r.Next(1000, 2000); 

       DateTime end = DateTime.Now.AddMilliseconds(num); 
       while (end > DateTime.Now) 
       { 
       } 

       Console.WriteLine(_count.ToString() + " : Done!"); 
      } 
      catch 
      { 
      } 
      finally 
      { 
       _work = null; 
      } 
     } 

     public void Dispose() 
     { 
      this._work = null; 
     } 

    } 
+0

你能使用.net 4.0嗎?我問,因爲語法會更乾淨。 – Xantix 2012-07-27 03:00:46

+0

不幸的是我僅限於.net 2.0 – 2012-07-27 03:05:32

回答

0

我不知道很多關於C#,但多線程的整體思路是,你有多個線程獨立執行,你可以永遠不知道哪一個會結束更早(和你不應該期望更早線程更早結束)。

一種解決方法是在處理線程中寫出完成消息,讓處理線程在某處設置標誌(可能是列表中沒有元素=沒有生成線程),並且有一個單獨的線程打印出完成該列表中的標誌的消息庫,並報告上一個標誌連續「完成」的位置。

老實說,我不覺得你這樣打印完成消息是否合理。我認爲改變設計會讓這種無意義的「功能」變得更好。

1

可能有一個比我使用的更簡單的方法,(我習慣於.NET 4.0)。

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Threading; 

namespace ConsoleApplication5 
{ 
    class Program 
    { 
     public static readonly int numOfTasks = 100; 

     public static int numTasksLeft = numOfTasks; 

     public static readonly object TaskDecrementLock = new object(); 

     static void Main(string[] args) 
     { 
      DoRead dr = new DoRead(); 

      dr.StartReading(); 

      int tmpNumTasks = numTasksLeft; 

      while (tmpNumTasks > 0) 
      { 
       Thread.Sleep(1000); 
       tmpNumTasks = numTasksLeft; 
      } 


      List<string> strings = new List<string>(); 

      lock(DoRead.locker) 
      { 
       for (int i = 1; i <= Program.numOfTasks; i++) 
       { 
        strings.Add(DoRead.dicto[i]); 
       } 
      } 

      foreach (string s in strings) 
      { 
       Console.WriteLine(s); 
      } 

      Console.ReadLine(); 
     } 

     public class DoRead 
     { 

      public static readonly object locker = new object(); 

      public static Dictionary<int, string> dicto = new Dictionary<int, string>(); 

      public DoRead() 
      { 
      } 

      public void StartReading() 
      { 
       int i = 1; 

       while (i <= Program.numOfTasks) 
       { 
        Runner r = new Runner(i, "Work" + i.ToString()); 
        r.StartThread(); 
        i += 1; 
       } 


      } 
     } 

     internal class Runner : System.IDisposable 
     { 
      int _count; 
      string _work = ""; 

      public Runner(int Count, string Work) 
      { 
       _count = Count; 
       _work = Work; 
      } 

      public void StartThread() 
      { 
       ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this); 
      } 

      public static void runThreadInPool(object obj) 
      { 
       Runner theRunner = ((Runner)obj); 
       string theString = theRunner.run(); 

       lock (DoRead.locker) 
       { 
        DoRead.dicto.Add(theRunner._count, theString); 
       } 

       lock (Program.TaskDecrementLock) 
       { 
        Program.numTasksLeft--; 
       } 
      } 

      public string run() 
      { 
       try 
       { 
        Random r = new Random(); 
        int num = r.Next(1000, 2000); 

        Thread.Sleep(num); 

        string theString = _count.ToString() + " : Done!"; 

        return theString; 

       } 
       catch 
       { 
       } 
       finally 
       { 
        _work = null; 
       } 

       return ""; 
      } 

      public void Dispose() 
      { 
       this._work = null; 
      } 

     } 
    } 
} 

基本上,我將要從每個任務打印的字符串存儲到索引是任務#的字典中。 (我使用鎖來訪問字典安全)。

接下來,讓主程序等到所有後臺線程完成後,我用另一個鎖定的NumTasksLeft變量訪問。

我在Runner的回調中添加了東西。

使用繁忙循環是不好的做法,所以我將它改爲了Thread.Sleep(num)語句。

只需將numOfTasks更改爲10000以符合您的示例。

我按順序將返回的字符串拖出字典,然後將其打印到屏幕上。

我相信你可以重構這個來移動或以其他方式處理全局變量,但這是有效的。

而且,你可能已經注意到我沒有在命令

tmpNumTasks = numTasksLeft; 

這是線程使用該鎖,因爲numTasksLeft是其原子讀取32位計算機和更高的一個int。

0

通常情況下,這些要求會以增加的序列號來滿足,就像您已經完成的那樣。

通常,來自處理線程的輸出通過包含所有無序結果對象的列表(或字典)的過濾器對象進行饋送,'將其保留'直到具有較低序列的所有結果 - 號碼進來了。再次,類似於你已經完成的工作。

什麼是沒有必要的是任何一種睡眠()循環。工作線程本身可以操作過濾器對象(可能會產生鎖),或者工作線程可以將結果生成器 - 消費者 - 將結果排隊到運行亂序過濾器的「輸出線程」。

該方案適用於合併工作線程,即。那些沒有持續創建/終止/銷燬開銷。