2012-08-16 178 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