2010-12-20 49 views
1

這裏是類:如何同步這兩個線程?

public class Ticker 
{ 
    public event EventHandler Tick; 
    public EventArgs e = null; 
    public void TickIt() 
    { 
     while (true) 
     { 
      System.Threading.Thread.Sleep(300); 
      if (Tick != null) 
      { 
       Tick(this, e); 
      } 
     } 
    } 

我運行在Windows窗體兩個線程:

public partial class Form1 : Form 
{ 
    Ticker ticker1 = new Ticker(); 
    Ticker ticker2 = new Ticker(); 
    Thread t; 
    Thread t1; 

    public Form1() 
    { 
     InitializeComponent(); 
     ticker1.Tick += ticker1_Tick; 
     ticker2.Tick += ticker2_Tick; 

     t = new Thread(new ThreadStart(ticker1.TickIt)); 
     t1 = new Thread(new ThreadStart(ticker2.TickIt))); 
     t.Start(); 
     t1.Start(); 

    } 
    public void ticker1_Tick(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.BeginInvoke((MethodInvoker)delegate 
      { 
       ticker1_Tick(sender, e); 
      }); 
      return; 
     } 


     richTextBox1.Text += "t1 "; 
    } 
    public void ticker2_Tick(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.BeginInvoke((MethodInvoker)delegate 
      { 
       ticker2_Tick(sender, e); 
      }); 
      return; 
     } 


     richTextBox2.Text += "t2 "; 
    } 

的問題是後幾秒鐘線程t超前T1的幾個蜱。

首先爲什麼會發生這種情況,它沒有任何意義,因爲每個線程在滴答之前應該等待300毫秒?

其次,我怎樣才能同步這兩個線程,所以他們同時打勾,一個不領先於另一個?

我不能在while循環之前放置一個鎖,那麼只有一個線程將會運行,而另一個線程將被鎖定。在別處放置一個鎖並不會改變任何東西。

+2

是你Tick()方法線程安全....? – 2010-12-20 02:15:00

+0

我不確定,如果不是,我不知道如何讓它變得安全。 Tick是一個事件:公共事件EventHandler Tick; – JohnCoSystem 2010-12-20 02:22:43

+1

您應該顯示整個可執行程序。請參閱[簡短,獨立,正確(可編譯),示例](http://sscce.org/)中的指導原則。 – 2010-12-20 02:26:35

回答

2

我不認爲你可以信任的睡眠(300),讓您的線程運行相同的次數獨立...

有一兩件事你可以做的是有一箇中央計時器/滴答發生器在每個滴答信號上發出一個同步對象,並且線程函數只滴答一次,然後WaitsForObject用於從主線程生成下一個滴答,實際上有一個定時器並告訴線程同步地進行滴答。

另請注意,您訂閱線程函數事件的方式,您需要考慮處理函數中的競爭條件。每個方法都會在它自己的線程上運行(直到begininvoke),所以如果你訪問任何資源(類字段等),那麼這些都需要同步。忘記這些線索會發生什麼很容易。 :(

+0

可否請您詳細說明您的答案中最後一段的含義,謝謝。 – JohnCoSystem 2010-12-20 03:07:29

+0

更新了答案.... – Jaime 2010-12-20 03:16:18

2

如果你真的需要它們完全同步並按照一定的順序執行刻度,那麼你需要一些類似Jaime提到的中央計時器。如果你需要獨立的計時,但是想要防止由於睡眠不準確造成漂移,或者延遲增加到執行事件處理程序所需的時間時,類似這樣的事情就會起作用:

public class Ticker 
{ 
    public event EventHandler Tick; 
    public EventArgs e = null; 
    public void TickIt() 
    { 
     const int targetSleepTime = 300; 
     int nextTick = Environment.TickCount + targetSleepTime; 
     while (true) 
     { 
      System.Threading.Thread.Sleep(Math.Max(nextTick - Environment.TickCount, 0)); 
      if (Tick != null) 
      { 
       Tick(this, e); 
      } 
      nextTick += targetSleepTime; 
     } 
    } 
} 

只要記住Environment.TickCount在到達Int32.MaxValue時可以返回到Int32.MinValue。您需要額外的代碼來處理這個問題,或者可能將時間基於DateTime.UtcNow(比DateTime.Now更少的開銷)。

2

如何使用AutoResetEvent

class Program 
{ 
    static readonly AutoResetEvent thread1Step = new AutoResetEvent(false); 
    static readonly AutoResetEvent thread2Step = new AutoResetEvent(false); 

    static void Main(string[] args) 
    { 
     new Thread(new ThreadStart(Thread1Main)).Start(); 
     new Thread(new ThreadStart(Thread2Main)).Start(); 
    } 

    private static void Thread1Main() 
    { 
     for (int i = 0; i < int.MaxValue; i++) 
     { 
      Console.WriteLine("thread1 i=" + i); 
      thread1Step.Set(); 
      thread2Step.WaitOne(); 
     } 
    } 

    private static void Thread2Main() 
    { 
     for (int i = 0; i < int.MaxValue; i++) 
     { 
      Console.WriteLine("thread2 i=" + i); 
      thread2Step.Set(); 
      thread1Step.WaitOne(); 
     } 
    } 
} 
0

在您的Ticker類中,增加您的輪詢頻率並檢查系統計時器,直到達到您要查找的時間間隔。如果您可以以毫秒精度生活,則可以使用TickCountTicks,如果系統支持它,則可以使用StopWatch以獲得更高精度。

爲了使它們保持同步,他們需要一個啓動時間的通用參考。您可以將其作爲特定的未來時間點傳入,以開始同步或使用類似Tick模數100的內容。或者輪詢共享靜態標誌,表示何時開始。

您不能擁有絕對精度,因此從一開始就定義您可以使用的精度範圍,例如您的Ticker線程之間的正負5ms。

有一件事會幫助你啓動一個共享靜態的實例,並在你所有的日誌中回覆它的運行時間,以幫助你描述應用程序中的任何延遲。