2

Using lock, i know if there are subsequent call, the later call will be waiting until previous call finishes. What if I don't want to have subsequent call to wait and let it skip that block of code.

I currently create a keyboard on a device which listen to other device input. I have a timer checking the device's input in 500milliseconds.

keyboardTimer = new Timer(500);
            keyboardTimer.Elapsed += (sender, e) =>
            {  
                lock (RemoteControlPage.keyboardLock)
                { 
                    var keyboardStatus = LauncherClient.Instance.GetKeyboardStatus();

                    if (keyboardStatus.Visibility == KEYBOARDVISIBILITY.SHOW)
                    { 
                        //Show keyboard
                    }
                    else if (keyboardStatus.Visibility == KEYBOARDVISIBILITY.HIDE)
                    {
                        //Hide keyboard 
                    }
                    else {
                        Debug.WriteLine("Keyboard = Do Nothing");
                    }
                }
            }; 

In the code, I have a lock in case two or more call entering it each time. Elapsed will fire again even though the previous call not finished processing leading the later call waiting. Is there a way to skip from execution instead of waiting the previous call finished processing.

LittleFunny
  • 8,155
  • 15
  • 87
  • 198
  • @Rob The linked duplicate does not address skipping around a lock. – BradleyDotNET Dec 15 '16 at 00:15
  • @BradleyDotNET How so? The first line of code in the top answer does exactly that. `if (Interlocked.CompareExchange(ref _lockFlag, 1, 0) == 0){`, assuming they implement it properly with the flag. – Rob Dec 15 '16 at 00:18
  • @Rob It seems like there would still be a race condition on entering the if statement and doing Monitor.Enter, no? – BradleyDotNET Dec 15 '16 at 00:19
  • @BradleyDotNET No, because the flag is changed atomically – Rob Dec 15 '16 at 00:21
  • @Rob Never mind, it seems that would work as well . Still not sure its a duplicate though as the asker simply wants to check the locked state, not avoid blocking. – BradleyDotNET Dec 15 '16 at 00:22
  • Note that a lock is probably inappropriate here because it does not prevent re-entry on the same thread. The code appears to be UI related code so it should be running on the UI thread. If it is not running on the UI thread that is probably a bug as well. – Mike Zboray Dec 15 '16 at 01:16

2 Answers2

3

You can use the Monitor class:

keyboardTimer = new Timer(500);
keyboardTimer.Elapsed += (sender, e) =>
{  
    if (Monitor.TryEnter(RemoteControlPage.keyboardLock))
    { 
        try
        {
            var keyboardStatus = LauncherClient.Instance.GetKeyboardStatus();

            if (keyboardStatus.Visibility == KEYBOARDVISIBILITY.SHOW)
            { 
                //Show keyboard
            }
            else if (keyboardStatus.Visibility == KEYBOARDVISIBILITY.HIDE)
            {
                //Hide keyboard 
            }
            else
            {
                Debug.WriteLine("Keyboard = Do Nothing");
            }
        }
        finally
        {
            Monitor.Exit(RemoteControlPage.keyboardLock);
        }
    }
 }; 
InBetween
  • 32,319
  • 3
  • 50
  • 90
-1

All this synchronization biz is a pain to get right. If you don't want to re-enter in a very unambiguous, easily understandable way, just go for an asynchronous loop:

async Task CheckKeyboard(CancellationToken token = default(CancellationToken))
{
     while(!token.IsCancellationRequested)
     {
         //talk to the keyboard
         await Task.Delay(500); //or some cunningly figured out wait time
     }
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • I wouldn't say that using a simple TryEnter is a pain, but busy waiting certainly is. – Voo Dec 15 '16 at 15:30
  • @Voo "busy waiting"? What do you mean? – spender Dec 15 '16 at 15:31
  • @Voo `await Task.Delay(500);` isn't busy time. It's yielded time. – spender Dec 15 '16 at 15:38
  • It's still unnecessary overhead compared to just doing a simple TryEnter. Context switching isn't free. Also "easily understandable" is debatable - I don't see any possibility how using the default parameter won't end up in a endless loop since IsCancellationRequested will never be true. – Voo Dec 15 '16 at 15:54
  • @Voo Yes, it will carry on forever ***unless*** the user passes in a `CancellationToken`, which they would probably want to do. I understand your preference for `TryEnter`, but for me, using Thread synchronization primitives for something as simple as preventing overlapping Timer callbacks isn't that straightforward, and for me, it's something far more succinctly expressed in terms of an asynchronous loop. Worrying about the overhead of context switching seems like a bit of a moot point when this is only happening every half second. – spender Dec 15 '16 at 16:19