2012-08-16 206 views
4

所以我有這個程序試圖建立兩個不同的線程,thread1和thread2之間的通信。等待另一個線程

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

namespace Project1 
{ 
    class Class1 
    { 
     public static void thread1() 
     { 
      Console.WriteLine("1"); 
      Console.WriteLine("t2 has printed 1, so we now print 2"); 
      Console.WriteLine("t2 has printed 2, so we now print 3"); 
     } 

     public static void thread2() 
     { 
      Console.WriteLine("t1 has printed 1, so we now print 1"); 
      Console.WriteLine("t1 has printed 2, so we now print 2"); 
      Console.WriteLine("t1 has printed 3, so we now print 3"); 
     } 

     public static void Main() { 

      Thread t1 = new Thread(new ThreadStart(() => thread1())); 
      Thread t2 = new Thread(new ThreadStart(() => thread2())); 
      t1.Start(); 
      t2.Start(); 
      t2.Join(); 
      t1.Join(); 

     } 

    } 
} 

不過,我希望它發生,使得該行:

Console.WriteLine("1"); 

...最先被執行,而線程2只等待被執行這條線。那時,也只有這樣纔打印:該行打印

Console.WriteLine("t1 has printed 1, so we now print 1"); 

後,再然後纔將這一行:

Console.WriteLine("t2 has printed 1, so we now print 2"); 

...得到印刷,等等。所以我想更改代碼,使線程相互溝通,使線路得到印刷順序:

  Console.WriteLine("1"); // from t1 
      Console.WriteLine("t1 has printed 1, so we now print 1"); // from t2 
      Console.WriteLine("t2 has printed 1, so we now print 2"); // from t1 
      Console.WriteLine("t1 has printed 2, so we now print 2"); // from t2 
      Console.WriteLine("t2 has printed 2, so we now print 3"); // from t1 
      Console.WriteLine("t1 has printed 3, so we now print 3"); // from t2 

我明白做什麼鎖,但它僅適用,如果兩個不同的線程是運行在相同的功能上。但是,在這裏,這兩個函數是不同的,因此我不能在這裏使用鎖。

任何想法?

+0

只是在鎖記,因爲我下面提到的,你的線程不需要處於相同的功能。只要在同一進程中運行,鎖可用於同步線程**。如果您嘗試同步不同進程中的線程(例如同步兩個不同的程序),則需要使用Mutex。 – 2012-08-16 16:34:25

回答

8

它看起來像你需要Monitor.WaitMonitor.Pulse。線程上有一個free eBook(可能有很多,但這個幫助我)。

你可以使用一個靜態對象來鎖定,然後讓你的線程調用Monitor.Pulse來表示他們「完成了他們的轉身」,Monitor.Wait「等待他們的下一個回合」。下面是使用你的基本代碼示例實現:

public class Class1 
{ 
    // Use this to syncrhonize threads 
    private static object SyncRoot = new object(); 
    // First "turn" goes to thread 1 
    private static int threadInControl = 1; 

    public static void thread1() 
    { 
     lock(SyncRoot) // Request exclusive access to SyncRoot 
     { 
      Console.WriteLine("1"); 
      GiveTurnTo(2); // Let thread 2 have a turn 
      WaitTurn(1); // Wait for turn to be given to thread 1 
      Console.WriteLine("t2 has printed 1, so we now print 2"); 
      GiveTurnTo(2); // Let thread 2 have a turn 
      WaitTurn(1); // Wait for turn to be given to thread 1 
      Console.WriteLine("t2 has printed 2, so we now print 3"); 
      GiveTurnTo(2); // Let thread 2 have a turn 
     } 
    } 

    public static void thread2() 
    { 
     lock(SyncRoot) // Request exclusive access to SyncRoot 
     { 
      WaitTurn(2); // Wait for turn to be given to thread 2 
      Console.WriteLine("t1 has printed 1, so we now print 1"); 
      GiveTurnTo(1); // Let thread 1 have a turn 
      WaitTurn(2); // Wait for turn to be given to thread 2 
      Console.WriteLine("t1 has printed 2, so we now print 2"); 
      GiveTurnTo(1); // Let thread 1 have a turn 
      WaitTurn(2); // Wait for turn to be given to thread 2 
      Console.WriteLine("t1 has printed 3, so we now print 3"); 
      GiveTurnTo(1); // Let thread 1 have a turn 
     } 
    } 

    // Wait for turn to use SyncRoot object 
    public static void WaitTurn(int threadNum) 
    { 
     // While(not this threads turn) 
     while (threadInControl != threadNum) 
     { 
      // "Let go" of lock on SyncRoot and wait utill 
      // someone finishes their turn with it 
      Monitor.Wait(SyncRoot); 
     } 
    } 

    // Pass turn over to other thread 
    public static void GiveTurnTo(int nextThreadNum) 
    { 
     threadInControl = nextThreadNum; 
     // Notify waiting threads that it's someone else's turn 
     Monitor.Pulse(SyncRoot); 
    } 

    public static void void Main() 
    { 
     Thread t1 = new Thread(new ThreadStart(() => Class1.thread1())); 
     Thread t2 = new Thread(new ThreadStart(() => Class1.thread2())); 
     t1.Start(); 
     t2.Start(); 
     t2.Join(); 
     t1.Join(); 
    } 
} 

至於使用lock keyword,它不侷限於同步的相同函數內。鎖「保證」對一個資源(對象)對單個線程的獨佔訪問(獨佔,我的意思是一次只有一個線程可以獲得該資源上的鎖,鎖不會阻止其他線程簡單地訪問該對象本身) 。

爲了簡化它,lock(someObject)就像是一個線程正在使用someOject,然後等待,直到它前面的所有其他線程都已經完成輪到它再繼續。線程結束其「轉」,當它離開鎖語句的範圍(除非你添加像Monitor.PulseMonitor.Wait的東西)。

+1

+1鏈接到一本夢幻般的書 – Kell 2012-08-16 15:51:52

+0

喬恩,非常感謝這個提示! – 2012-08-16 16:01:16

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

namespace Project1 
{ 
    class Class1 
    { 
     private static ManualResetEvent mre1 = new ManualResetEvent(false); 
     private static ManualResetEvent mre2 = new ManualResetEvent(false); 

     public static void thread1() 
     { 
      Console.WriteLine("1"); 
      mre2.Set(); 
      mre1.WaitOne(); 
      Console.WriteLine("t2 has printed 1, so we now print 2"); 
      mre2.Set(); 
      mre1.WaitOne(); 
      Console.WriteLine("t2 has printed 2, so we now print 3"); 
     } 

     public static void thread2() 
     { 
      mre2.WaitOne(); 
      Console.WriteLine("t1 has printed 1, so we now print 1"); 
      mre1.Set(); 
      mre2.WaitOne(); 
      Console.WriteLine("t1 has printed 2, so we now print 2"); 
      mre1.Set(); 
      mre2.WaitOne(); 
      Console.WriteLine("t1 has printed 3, so we now print 3"); 
     } 

     public static void Main() { 

      Thread t1 = new Thread(new ThreadStart(() => thread1())); 
      Thread t2 = new Thread(new ThreadStart(() => thread2())); 

      t1.Start(); 
      t2.Start(); 

      while (true) { 
       Thread.Sleep(1); 
      } 

     } 

    } 
} 

謝謝你們。我想我現在有解決方案!

+0

如果你已經找到了解決問題的答案(如果你願意的話,它可以是你自己的解決方案,儘管看起來@凱爾的回答是讓你在這裏的答案),請在此頁面接受解決方案。 – 2012-08-16 16:37:09

1

從包含在高級線程教程由喬阿爾巴哈利先生等待和脈衝部分信令的Two-Way Signaling and Races節給出的示例:

static readonly object locker = new object(); 
private static bool ready, go; 

public static void Thread1() 
{ 
    IEnumerable<Action> actions = new List<Action>() 
    { 
    () => Console.WriteLine("1"), 
    () => Console.WriteLine("t2 has printed 1, so we now print 2"), 
    () => Console.WriteLine("t2 has printed 2, so we now print 3") 
    }; 

    foreach (var action in actions) 
    { 
    lock (locker) 
    { 
     while (!ready) Monitor.Wait(locker); 
     ready = false; 
     go = true; 
     Monitor.PulseAll(locker); 
     action(); 
    } 
    } 
} 

public static void Thread2() 
{ 
    IEnumerable<Action> actions = new List<Action>() 
    { 
    () => Console.WriteLine("t1 has printed 1, so we now print 1"), 
    () => Console.WriteLine("t1 has printed 2, so we now print 2"), 
    () => Console.WriteLine("t1 has printed 3, so we now print 3") 
    }; 

    foreach (var action in actions) 
    { 
    lock (locker) 
    { 
     ready = true; 
     Monitor.PulseAll(locker); 
     while (!go) Monitor.Wait(locker); 
     go = false; 
     action(); 
    } 
    } 
} 

private static void Main(string[] args) 
{ 
    Thread t1 = new Thread(new ThreadStart(() => Thread1())); 
    Thread t2 = new Thread(new ThreadStart(() => Thread2())); 
    t1.Start(); 
    t2.Start(); 
    t2.Join(); 
    t1.Join(); 
} 
+0

嘿,謝謝一堆! – 2012-08-16 16:12:17

+0

採用'Monitor.Pulse()'和'Monitor.Wait()'思路的好方法,並減少添加更多操作所需的代碼量。 – 2012-08-16 16:41:18

相關問題