如何确保只调用一部分代码(包含异步)?

Vit*_*nko 7 c# multithreading thread-safety windows-phone-7

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

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

起初,我试图用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);
                }
Run Code Online (Sandbox Code Playgroud)

然而,在Exit(),我得到例外:

"System.Threading.SynchronizationLockException"类型的第一次机会异常

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

xan*_*tos 14

上课:

private int entered = 0;
Run Code Online (Sandbox Code Playgroud)

并在方法中:

if (Interlocked.Increment(ref entered) != 1)
{
    return;
}
Run Code Online (Sandbox Code Playgroud)

只有第一次调用该方法才能entered从0 更改为1.其他调用将使其为2,3,4,5 ......

显然,entered如果你希望你的方法可以被重新定义,你需要重置一些东西......

Interlocked.Exchange(ref entered, 0);
Run Code Online (Sandbox Code Playgroud)

在成功调用该方法结束时.

啊......并且不可能使用lock/ Monitor.*around await,因为方法的当前线程可以更改,而几乎所有同步库都期望您用来输入锁的线程与用于退出的线程相同锁.

你甚至可以使用Interlocked.CompareExchange()......

if (Interlocked.CompareExchange(ref entered, 1, 0) != 0)
{
    return;
}
Run Code Online (Sandbox Code Playgroud)

要输入的第一个线程将能够将输入的值从0更改为1,并且它将接收旧值0(因此,如果在其余代码中继续执行if并继续执行).其他线程将失败CompareExchange并看到"当前"值为1,因此输入if和退出方法

  • @VitaliiVasylenko不,你需要使用`if(Interlocked.CompareExchange(ref entered,1,0)!= 0)return;`.要输入的第一个能够从0更改为1并接收旧值(0).其他人未通过更改并收到当前值(1). (3认同)