所以,我有一个方法,它包含对服务器的异步调用。 该代码是从第三方工具调用的,它有时会从不同的线程连续多次调用相同的方法,所以我不能影响它。
我想确定的是我的方法被调用一次,然后应该忽略另一个调用。
起初,我试图用bool isBusy来锁定(locker),但是我不满意,因为异步请求仍然从第二个线程被多次调用,这很快就能看到isBusy = true;
然后,我尝试了Monitor
object obj = new object();
Monitor.TryEnter(obj);
try
{
var res = await _dataService.RequestServerAsync(SelectedIndex, e.StartIndex, e.Count);
****
}
finally
{
Monitor.Exit(obj);
}
然而,在Exit()
,我得到例外:
类型的第一次机会异常 'System.Threading.SynchronizationLockException'
还有其他方法可以保证只执行一次代码吗?
答案 0 :(得分:14)
加入课堂:
private int entered = 0;
并在方法中:
if (Interlocked.Increment(ref entered) != 1)
{
return;
}
只有第一次调用该方法才能将entered
从0更改为1.其他人将使其成为2,3,4,5 ......
如果您希望自己的方法可以被重新定位,显然您需要重置entered
...
Interlocked.Exchange(ref entered, 0);
在成功调用该方法结束时。
啊......并且无法在lock
周围使用Monitor.*
/ await
,因为该方法的当前线程可以更改,而几乎所有同步库期望用于输入锁的线程与用于退出锁的线程相同。
您甚至可以使用Interlocked.CompareExchange()
...
if (Interlocked.CompareExchange(ref entered, 1, 0) != 0)
{
return;
}
要输入的第一个线程将能够将输入的值从0更改为1,并且它将接收旧值0(因此,如果在其余代码中继续if并继续)。其他线程将失败CompareExchange
并看到“当前”值为1,因此输入if和退出方法
答案 1 :(得分:3)
如果你想同时使用相同的方法限制多个线程,那么我会使用Semaphore
类来促进所需的线程限制;这是怎么......
信号量就像一个卑鄙的夜总会保镖,它一直提供俱乐部容量,不允许超过这个限制。一旦俱乐部满员,没有其他人可以进入...队列在外面建立起来。然后当一个人离开时,另一个人可以进入(类比感谢J. Albahari)。
值为1的Semaphore
等同于Mutex
或Lock
,但Semaphore
没有所有者,因此它是线程无知的。任何主题都可以在Release
上调用Semaphore
,而使用Mutex
/ Lock
只有获得Mutex
/ Lock
的主题可以释放它
现在,对于您的情况,我们可以使用Semaphore
来限制并发性并防止太多线程一次执行特定代码。在下面的示例中,五个线程尝试进入夜总会,只允许进入三个......
class BadAssClub
{
static SemaphoreSlim sem = new SemaphoreSlim(3);
static void Main()
{
for (int i = 1; i <= 5; i++)
new Thread(Enter).Start(i);
}
// Enfore only three threads running this method at once.
static void Enter(int i)
{
try
{
Console.WriteLine(i + " wants to enter.");
sem.Wait();
Console.WriteLine(i + " is in!");
Thread.Sleep(1000 * (int)i);
Console.WriteLine(i + " is leaving...");
}
finally
{
sem.Release();
}
}
}
请注意,SemaphoreSlim
是Semaphore
类的轻量级版本,并且会产生大约四分之一的开销。它足以满足您的需求。
我希望这会有所帮助。