8

所以,我有一个方法,其中包含对服务器的异步调用。该代码是从 3rd 方工具调用的,它有时会从不同的线程连续多次调用相同的方法,所以我不能影响它。

我要确定的是我的方法被调用一次,然后,另一个调用应该被忽略。

起初,我尝试使用 bool isBusy 锁定(locker),但这并不让我满意,因为仍然从第二个线程调用了几次异步请求,这足以看到 isBusy=true;

然后,我尝试了监视器

                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”类型的第一次机会异常

有没有其他方法可以保证代码只执行 1 次?

4

2 回答 2

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 并退出该方法

于 2013-08-20T11:20:10.223 回答
4

如果您确实想同时使用相同的方法限制多个线程,那么我将使用Semaphore该类来促进所需的线程限制;就是这样...

信号量就像一个普通的夜总会保镖,它已经提供了一个俱乐部容量,并且不允许超过这个限制。一旦俱乐部坐满了,就没有其他人可以进入了……外面已经排起了长队。然后当一个人离开时,另一个人可以进入(感谢 J. Albahari 的类比)。

值为 1 的ASemaphore等价于 aMutexLock,只是它Semaphore没有所有者,因此它是线程无知的。任何线程都可以调用ReleaseaSemaphore而使用Mutex/Lock只有获得Mutex/的线程Lock才能释放它。

现在,对于您的情况,我们可以使用Semaphores 来限制并发性并防止太多线程一次执行特定的一段代码。在下面的示例中,五个线程尝试进入一个只允许三个人进入的夜总会……

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并且会产生大约四分之一的开销。这足以满足您的要求。

我希望这有帮助。

于 2013-08-20T11:26:12.243 回答