所以我有这个程序试图在两个不同的线程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");
...首先执行,而thread2只是等待执行此行。然后才会打印出来:
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 :(得分:9)
您似乎需要Monitor.Wait和Monitor.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
,然后等到它前面的所有其他线程完成转弯才继续。线程在离开lock语句的范围时结束其“转向”(除非您添加Monitor.Pulse
或Monitor.Wait
之类的内容。)
答案 1 :(得分:3)
使用ManualResetEvent。在线程2中等待它并在Console.WriteLn()之后在thread1中设置它 http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx
答案 2 :(得分: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);
}
}
}
}
谢谢你们。我想我现在有了解决方案!
答案 3 :(得分:1)
来自Joe Albahari先生的高级线程教程中包含的“等待和脉冲信号”部分的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();
}