我知道在开发多线程应用程序时,您必须使用例如监视器或锁来同步对共享内存的访问。
问题
您如何告诉等待进程(proc2)使用锁定代码块(proc1)的进程已完成使用该代码?
我知道在开发多线程应用程序时,您必须使用例如监视器或锁来同步对共享内存的访问。
您如何告诉等待进程(proc2)使用锁定代码块(proc1)的进程已完成使用该代码?
您是在谈论真正的操作系统进程,还是只是进程中的任务?
如果它只是流程中的任务,您通常只需让锁为您完成工作。第二个任务尝试获取第一个任务当前拥有的锁,然后阻塞。当第一个任务释放锁时,第二个任务自动解锁。
如果你想让第二个线程可以选择先做其他工作,你可以Monitor.TryEnter
改用 - 虽然这有点复杂。
如果您想等待,但您有比锁定更复杂的要求,那么Monitor.Pulse/PulseAll/Wait
可能需要。有关示例,请参见本页的后半部分。
如果您实际上是在谈论进程,那么您将需要使用系统范围的结构,例如Mutex
. 这些比进程内 .NET 监视器“更重”,但允许进程间协调。
其他进程通常被阻塞,等待抢锁。当您的第一个进程释放它时,第二个进程可以抓取它。
换句话说,在典型情况下,您不会“告诉”另一个线程去,而是停止阻止它去。
(在某些情况下,您确实想告诉线程去,通常由 AutoResetEvent 或 ManualResetEvent 处理)
为此,您需要利用 C# 中可用的各种同步对象。可用同步对象的一些示例是System.Threading.Mutex、System.Threading.Semaphore、System.Threading.ManualResetEvent和System.Threading.AutoResetEvent(这不是一个详尽的列表,但它是基础知识)。您需要的确切同步对象取决于您的特定需求。
互斥锁可能是最简单的使用,所以我举个例子。假设我有两个函数在两个不同的线程中运行(我将坚持使用您自己的示例,proc1 和 proc2)。两个函数都需要访问同一个变量 foo。在每个访问 foo 的函数中,您需要先“锁定”您的互斥锁。然后做任何你需要做的事情,然后解锁它。
例如:
class bar
{
private int foo;
private System.Threading.Mutex lock = new System.Threading.Mutex;
public void proc1()
{
lock.WaitOne();
// do stuff to foo
lock.ReleaseMutex();
}
public void proc2()
{
lock.WaitOne();
// do stuff to foo
lock.ReleaseMutex();
}
};
使用此方法,proc1 将执行。它将尝试“抓住”互斥体(锁)。如果互斥锁已经被抓取,proc1 将进入睡眠状态,直到互斥锁被另一个线程“释放” - proc1 将坐下来等待,什么都不做(并且不吃任何 CPU 周期!),直到互斥锁被释放。然后它会锁定它并做它的事情。在 proc1 完成之前,没有其他线程能够获取互斥锁。
另一种选择是使用事件。C# 提供两种类型的事件 - 手动重置和自动重置。我将在我的示例中使用手动重置事件。
class bar
{
private System.Threading.ManualResetEvent event = new System.Threading.ManualResetEvent;
public void proc1()
{
// do stuff
event.Set();
}
public void proc2()
{
event.WaitOne();
event.Reset();
// do stuff
}
};
在此示例中,当 proc2 运行时,它会在遇到“event.WaitOne”时进入睡眠状态,并且在 proc1“设置”事件之前不使用 CPU 周期。设置事件会导致 proc2 被唤醒,现在它可以做它的事情了。因为这是一个手动重置事件,所以事件将保持在“设置”状态,直到调用 event.Reset()。
根据多线程的性质,Thread.Join 可以解决问题。它阻塞调用线程,直到线程终止,同时继续执行标准消息泵送。