Home > Software engineering >  Form method stops Task with cancellation token and waits for task to complete. Stop Method never yie
Form method stops Task with cancellation token and waits for task to complete. Stop Method never yie

Time:04-13

I have a Service Class that Starts and Stops a task that is intended to run in the background behind a forms app.

When stopping the monitor: Once the Stop() method is called, no breakpoints are hit on the Monitor Task and IsStarted never becomes false' and is stuck in an infinite loop.

What is wrong with this? Why does the Monitor Task seeming stop running? How do I fix this?


        private volatile bool IsStarted = false;

        public void Start(PartList partList, UnioServiceSettings settings, Action<string, string, string> createSummaryFile)
        {
            _Settings = settings;
            _PartList = partList;
            _cts = new CancellationTokenSource();
            Task.Run(async() => await Monitor(_cts.Token));  //no difference:.ConfigureAwait(false);
            _ProgressMessage?.Report("Monitor Started...");
            IsStarted = true;
        }
            
        public async Task<bool> Stop()
        {
            if (IsStarted)
            {
                _ProgressMessage?.Report("Stopping Monitor...");
                _cts.Cancel();
                while (IsStarted) //this loop runs forever
                {
                    await Task.Delay(1000);  //.ConfigureAwait(false);? no difference
                }
                return true;
            }
            else
            {
                _ProgressMessage?.Report("Cannot stop Monitor. It has not been started.");
                return false;
            }
        }

        private async Task Monitor(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                 //do stuff
                 await Task.Delay(_Settings.DelayMs,token).ConfigureAwait(false);
            }
            IsStarted = false;
            return;
        }

CodePudding user response:

There is a race condition here:

Task.Run(async() => await Monitor(_cts.Token));
_ProgressMessage?.Report("Monitor Started...");
IsStarted = true;

It is possible that the Task created by the Task.Run method will complete on another thread before the current thread calls IsStarted = true;. So eventually the IsStarted will settle at the value true without a Task running. To solve this problem you could transfer the responsibility of mutating the IsStarted field exclusively to the Monitor method itself:

private async Task Monitor(CancellationToken token)
{
    IsStarted = true;
    try
    {
        _ProgressMessage?.Report("Monitor Started...");
        while (true)
        {
            token.ThrowIfCancellationRequested();
            //do stuff
            await Task.Delay(_Settings.DelayMs,token).ConfigureAwait(false);
        }
    }
    finally
    {
        IsStarted = false;
    }
}

I don't know if this race condition is the cause of your problem though.

  • Related