ThymiosK ThymiosK - 3 years ago 145
C# Question

Exiting from async infinite loops

I have initiated some async infinite loops in my WinForm application, but each time I am trying to break out of them, the program hangs up. I have read some similar topics where people suggested using CancellationTokens, but I am not able to adapt them to my needs. Here is the relevant part of my code.

static bool processStop = false;
static bool processStopped = false;

//Called once
private async void ProcessData()
{
while (!processStop)
{
await Task.Run
(
() =>
{
//Do stuff and call regular not async methods
}
);
}
processStopped = true;
}

//Button click handler to exit WinForm
btnExit.Click += (senders, args) =>
{
processStop = true;
//Programm hangs up here

while (!processStopped);

FormMain.Close();
}


Edited the code

The variables are static.

The Close method is the default Close() method for Forms.

Answer Source

The problem is that the call to Task.Run continues on the main thread. processStop = true; and while (!processStopped); execute synchronously one after the other. This doesn't let the ProcessData method continue its execution and a deadlock occures.
I see a couple of solutions:

  • Use ConfigureAwait(false) with Task.Run:

    private async void ProcessData()
    {
        while (!processStop)
        {
            await Task.Run
            (
                () =>
                {
                    //Do stuff and call regular not async methods
                }
            ).ConfigureAwait(false);
        }
        processStopped = true;
    }
    

    This will cause the ProcessData to continue on a thread pool and you already use a thread pool by calling Task.Run, so it is not a great solution

  • Wrap the whole process in Task.Run:

    static volatile bool processStop = false;
    static volatile bool processStopped = false;
    
    //Called once 
    private async void ProcessData()
    {
        await Task.Run(() =>
        {
            while (!processStop)
            {
                ...
            }
            processStopped = true;
        });
    }
    

    This would require changing the form of the method passed to work with the loop in it.

  • Make ProcessData a synchronous method to process CPU-intensive tasks and call it properly. CancellationToken would be the preferred way to cancel the task:

    private void ProcessData(CancellationToken token)
    {
        while(!token.IsCancellationRequested)
        {                
            // do work
        }
    }
    

    And call it with this:

    Task processingTask;
    CancellationTokenSource cts;
    
    void StartProcessing()
    {
        cts = new CancellationTokenSource();
        processingTask = Task.Run(() => ProcessData(cts.Token), cts.Token);
    }
    
    btnExit.Click += async (senders, args) =>
    {
        cts.Cancel();
        try
        {
            await processingTask;
        }
        finally
        {
            FormMain.Close();
        }                
    }
    
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download