Search code examples
c#winformstaskcancellationcancellationtokensource

Task status with CancellationToken doesn't change


I am implementing a downloader class and i am using Tasks, first i created a task and passed the CancellationToken with it, when i cancel the task, the Task is canceled but the status of the task is still Running, even after i call ThrowIfCancellationRequested() the staus is still the same. Isn't the status suppose to be Canceled or Faulted in case i throw the ThrowIfCancellationRequested().

Here is my code:

    public void Start()
    {
        //Start download
        Status = DownloadStatus.Running;
        CancellTS = new CancellationTokenSource();
        CancelT = CancellTS.Token;
        TaskStartDownload = Task.Run(() => { StartDownload(); }, CancelT);
    }

   private void StartDownload()
    {
        //Raise event download start
        if (OnDownloadStart != null)
            OnDownloadStart(this, new DownloadEventArgs(Id, 0, 0, 0, Controls, false));

        byte[] buffer = new byte[4096];
        long bytesToRead = Size;

        Timer.Start();
        while (!CancelT.IsCancellationRequested)
        {
            int currentBytesRead = Reader.Read(buffer, 0, buffer.Length);

            Speed = currentBytesRead == 0 ? 0 : ((float)BytesRead / 1024) / Timer.Elapsed.TotalSeconds;
            ProgressPercentage = ProgressPercentage + (currentBytesRead * 1.0 / Size) * 100;
            TimeRemaining = (int)(((double)(Size - BytesRead) / 1024) / Speed);

            if (currentBytesRead == 0)
            {
                Status = DownloadStatus.Completed;

                //Raise event download start
                if (OnDownloadComplete != null)
                    OnDownloadComplete(this, new DownloadEventArgs(Id, 100, 0, 0, Controls, false));

                break;
            }

            Writer.Write(buffer, 0, currentBytesRead);
            Writer.Flush();

            //Raise progress event
            this.OnDownloadProgress(this, new DownloadEventArgs(Id, Speed, ProgressPercentage, TimeRemaining, Controls, false));

            BytesRead += currentBytesRead;

            if (Status == DownloadStatus.Pause || Status == DownloadStatus.Cancel)
                break;
        }
        Timer.Stop();

        Reader.Close();
        Reader = null;
        Writer.Close();

        if (CancelT.IsCancellationRequested)
        {
            try
            {
                CancelT.ThrowIfCancellationRequested();
            }
            catch (Exception ex)
            {
                Status = DownloadStatus.Cancel;

                //Raise event download start
                if (OnDownloadCancel != null)
                    this.OnDownloadCancel(this, new DownloadEventArgs(Id, 0, 0, 0, Controls, true));

                File.Delete(FileNameWithPath);
            }
        }

        TaskPrepareDownload.Dispose();
        TaskStartDownload.Dispose();
    }

        public void Cancel()
    {
        if (TaskStartDownload == null)
        {
            return;
        }
        else
        {
            CancellTS.Cancel();
        }
    }

Solution

  • You should not Swallow the exception after you call ThrowIfCancellationRequested(), so the caller (the Start() method) can be notified of the change in task status. If you swallow the exception, the task is considered to be successfully completed (Status = RanToCompletion), or still Running if you poll it immediately after you fire the cancellation.

    The exception handler should be placed in the caller when you wait for task to be completed.

    Please refer to the example from MSDN Task Cancellation.