How to ensure that part of the code (which contains an asynchronous call) is called only once?

So, I have a method that contains an asynchronous call to the server. This code is called from a third-party tool, which sometimes sometimes calls the same method several times in a row from different threads, so I can not affect this.

I want to make sure that my method is called once, and then other calls should be ignored.

At first I tried to lock (the locker) using bool isBusy, but this did not satisfy me, since the asynchronous request was still called several times from the second thread, which was fast enough to see isBusy = true;

Then I tried Monitor

object obj = new object(); Monitor.TryEnter(obj); try { var res = await _dataService.RequestServerAsync(SelectedIndex, e.StartIndex, e.Count); **** } finally { Monitor.Exit(obj); } 

However, on Exit() , I get an exception:

First random exception of type 'System.Threading.SynchronizationLockException'

Is there any other way to guarantee only one-time code execution?

+7
multithreading c # thread-safety windows-phone-7
source share
2 answers

Put in class:

 private int entered = 0; 

and in the method:

 if (Interlocked.Increment(ref entered) != 1) { return; } 

Only the first method call can change entered from 0 to 1. Others will do this 2, 3, 4, 5 ...

Obviously, you will need something to reset entered if you want your method to be restored ...

 Interlocked.Exchange(ref entered, 0); 

at the end of a successful method call.

Ah ... and itโ€™s impossible to use lock / Monitor.* Around await , because the current method thread can change, while almost all synchronization libraries expect that the thread you use to enter the lock is the same as what you use to exit blocking.

You can even use Interlocked.CompareExchange() ...

 if (Interlocked.CompareExchange(ref entered, 1, 0) != 0) { return; } 

The first input stream will be able to exchange the value entered from 0 to 1, and it will get the old value, 0 (thus, if and failure continues in the remaining code). Other threads will fail CompareExchange and see the "current" value of 1, so type if and exit the method

+14
source share

If you want to limit several threads to the same method at the same time, I would use the Semaphore class to facilitate stream limitation; that's how ...

A semaphore is like an average bouncer nightclub, it provides the club's throughput and cannot exceed this limit. Once the club is full, no one else can enter ... The line is being built outside. Then, when one person leaves another, he can enter (analogy thanks to J. Albahari).

A Semaphore with a value of one is equivalent to a Mutex or Lock value, except that the Semaphore does not have an owner to be threadlike. Any thread can call Release on the Semaphore , while with Mutex / Lock only the thread that received the Mutex / Lock can release it.

Now for your case, we can use Semaphore to limit concurrency and prevent too many threads from executing a particular piece of code at the same time. In the following example, five threads try to enter a nightclub that allows only three entrances ...

 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(); } } } 

Note that SemaphoreSlim is a lighter version of the Semaphore class and takes about a quarter of the overhead. it is enough for what you need.

Hope this helps.

+3
source share

All Articles