在开发过程中我经常遇到下一个问题:如果某个方法已经由一个线程执行 - 方法不能被另一个线程执行。另一个线程必须什么都不做 - 简单退出方法,因为它我不能使用“锁定”。通常,我这样解决这个问题:
private bool _isSomeMethodExecuted = false;
public void SomeMethod ()
{
if (!this._isSomeMethodExecuted) //check if method is already executed
{
this._isSomeMethodExecuted = true;
//Main code of method
this._isSomeMethodExecuted = false;
}
}
但是这段代码不是线程安全的:如果一个线程执行条件语句但是在set flag为true之前停止,而另一个线程可以执行条件 - 那么两个线程都在方法代码中。
是否有任何线程安全替代品?
答案 0 :(得分:2)
以下是线程安全的,并且如果该方法已在执行,则不会阻止 - 即使它在同一个线程上执行也很容易...这可以防止所有场景的重入。
private long _isSomeMethodExecuted = 0;
public void SomeMethod ()
{
if (Interlocked.Increment (ref this._isSomeMethodExecuted) == 1) //check if method is already executed
{
//Main code of method
}
Interlocked.Decrement (ref this._isSomeMethodExecuted);
}
答案 1 :(得分:1)
Monitor
为你完成这项工作,但锁是线程范围(因此可以打开递归调用!)。 lock
语句也使用Monitor
(使用阻止Enter
方法),但您可以改为使用TryEnter
方法:
if(Monitor.TryEnter(myLockObject))
{
try
{
DoSomething(); // main code
}
finally
{
Monitor.Exit(myLockObject);
}
}
TryEnter不会阻止,但会返回bool
,表示是否已成功获取锁定。
如果您希望递归调用不再输入主代码块,则应使用信号量。信号量使用计数器而不是锁定对象,因此您甚至无法从同一个线程重新进入:
class Program
{
private static Semaphore sem = new Semaphore(1, 1);
static void Main(string[] args)
{
MyMethod();
MyMethod();
}
private static void MyMethod()
{
if(sem.WaitOne(0))
{
try
{
Console.WriteLine("Entered.");
MyMethod(); // recursive calls won't re-enter
}
finally
{
sem.Release();
}
}
else
{
Console.WriteLine("Not entered.");
}
}
}