0

I am using async methods and a SemaphoreSlim object to wait for and detect a click. Eventually, on a different method being called, I want to stop the async method that is being run and continue as if the method completed.

Incase it affects the answer, I am using Windows Forms.

Here is a skeleton of my problem (not my actual code)

private SemaphoreSlim _semaphoreClick = new SemaphoreSlim(0, 1);

static void Main() 
{
    RunProgram();
    SomeFinishedFuncion();
}

async void RunProgram()
{
    object thing = await WaitForSemaphoreSlim();   
}

async Task<object> WaitForSemaphoreSlim() 
{
    object thing;
    // do stuff
    
    await _semaphoreClick.WaitAsync();
        
    // do stuff
    return thing;
}


void OnStopProgram() 
{
    // Do something here that will stop the RunProgram() 
    // which will make Main() continue to SomeFinishedFunction()
}

  • Can't you just make "thing" a public variable? Then call thing.Cancel() – David P Mar 01 '23 at 16:36
  • You say you're using WinForms, but you have `static void Main` and your code appears to be running before WinForms' synchronziation-context is setup... either this isn't your actual code, or you're not actually using WinForms. Also, **never** use `async void` _except_ for WinForms and WPF `EventHandler` methods - that's part of your problem: because it means you can't `await` your `RunProgram` method. – Dai Mar 01 '23 at 16:40
  • @Dai, sorry you're right that it isn't my actual code because I tried to write out a new skeleton code which simplified the problem – Shinglington Mar 01 '23 at 16:42

1 Answers1

4

Use a CancellationTokenSource and CancellationToken:

private SemaphoreSlim _semaphoreClick = new SemaphoreSlim(0, 1);
private CancellationTokenSource cts = new CancellationTokenSource();

static void Main() 
{
    RunProgram(cts.Token);
    SomeFinishedFuncion();
}

async void RunProgram(CancellationToken cToken)
{
    try
    {
        object thing = await WaitForSemaphoreSlim(cToken);
    }
    catch(OperationCanceledException)
    {
         //When a token is cancelled this exception is raised.
    }   
}

async Task<object> WaitForSemaphoreSlim(CancellationToken cToken) 
{
    object thing;
    // do stuff
    
    await _semaphoreClick.WaitAsync(cToken);
        
    // do stuff
    return thing;
}


void OnStopProgram() 
{
    cts.Cancel();
}

Side note: be careful using async void: Avoid Async Void

Alberto
  • 15,626
  • 9
  • 43
  • 56
  • 2
    It may be useful to do `when (cToken.IsCancellationRequested)` on the `catch`, so we don't accidentally catch other cancellations that were not expected. – Matthew Mar 01 '23 at 16:44
  • 3
    I think you actually want `async Task` and `RunProgram(cts.Token).Wait();` alternatively use the new `static async Task Main()` and `await RunProgram(cts.Token);` – Charlieface Mar 01 '23 at 17:09
  • Async Main came in 2017, https://devblogs.microsoft.com/dotnet/welcome-to-c-7-1/#async-main So surprised to see it's not yet adopted well in this answer. – Lex Li Mar 02 '23 at 08:50
  • @Charlieface It appears that the author of the question intended to create a fire-and-forget task which could be cancelled at a later time. – Alberto Mar 02 '23 at 09:05