Share via

System.Threading.Tasks.TaskCanceledException

jason heung 20 Reputation points
2026-05-27T07:00:01.91+00:00

我的代码:

using System.ComponentModel;

using System.Text;

using System.Threading;

using System.Threading.Tasks;

using System.Windows;

namespace CancelTokenTest

{

    public partial class MainWindow : Window

    {

        private CancellationTokenSource? _cancellationTokenSource;

        private Task? _runningTask;

        public MainWindow()

        {

            InitializeComponent();

            AppendLog("应用启动就绪");

        }

        private async void StartButton_Click(object sender, RoutedEventArgs e)

        {

            if (_runningTask != null && !_runningTask.IsCompleted)

            {

                AppendLog("已有任务正在运行,请先取消");

                return;

            }

            _cancellationTokenSource = new CancellationTokenSource();

            var token = _cancellationTokenSource.Token;

            StartButton.IsEnabled = false;

            CancelButton.IsEnabled = true;

            StatusText.Text = "任务运行中...";

            StatusText.Foreground = System.Windows.Media.Brushes.Orange;

            AppendLog("========== 开始执行任务 ==========");

            _runningTask = Task.Run(async () =>

            {

                try

                {

                    for (int i = 1; i <= 10; i++)

                    {

                        token.ThrowIfCancellationRequested();

                        

                        await Task.Delay(500, token);

                        

                        Dispatcher.Invoke(() =>

                        {

                            AppendLog($"步骤 {i}/10 完成");

                        });

                    }

                    Dispatcher.Invoke(() =>

                    {

                        StatusText.Text = "任务完成";

                        StatusText.Foreground = System.Windows.Media.Brushes.Green;

                        AppendLog("========== 任务成功完成 ==========");

                    });

                }

                catch (OperationCanceledException)

                {

                    Dispatcher.Invoke(() =>

                    {

                        StatusText.Text = "任务已取消";

                        StatusText.Foreground = System.Windows.Media.Brushes.Red;

                        AppendLog("========== 任务已取消 ==========");

                    });

                }

                catch (Exception ex)

                {

                    Dispatcher.Invoke(() =>

                    {

                        StatusText.Text = "任务出错";

                        StatusText.Foreground = System.Windows.Media.Brushes.Red;

                        AppendLog($"任务执行出错: {ex.Message}");

                    });

                }

                finally

                {

                    Dispatcher.Invoke(() =>

                    {

                        StartButton.IsEnabled = true;

                        CancelButton.IsEnabled = false;

                    });

                }

            }, token);

        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)

        {

            if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)

            {

                _cancellationTokenSource.Cancel();

                AppendLog("正在取消任务...");

            }

        }

        private void AppendLog(string message)

        {

            LogTextBox.AppendText($"{System.DateTime.Now:HH:mm:ss} - {message}\n");

            LogTextBox.ScrollToEnd();

        }

        protected override void OnClosing(CancelEventArgs e)

        {

            _cancellationTokenSource?.Cancel();

            _cancellationTokenSource?.Dispose();

            base.OnClosing(e);

        }

    }

}

控制台抛出引发的异常:“System.Threading.Tasks.TaskCanceledException”(位于 System.Private.CoreLib.dll 中)。我这个取消方式应该怎么修改?

Windows development | Windows API - Win32
0 comments No comments

Answer accepted by question author

Taki Ly (WICLOUD CORPORATION) 1,830 Reputation points Microsoft External Staff Moderator
2026-05-27T08:12:09.11+00:00

你好 @jason heung ,

我在本地测试了你的代码,并在调试控制台(Debug Console)中成功复现了完全相同的信息:Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll

User's image

不过请放心,你不需要担心。你的取消实现方式完全正确,并且符合最佳实践。你不需要做任何修改。

根据设计,.NET 的任务取消机制在任务被中止时(例如 Task.Delay(500, token)token.ThrowIfCancellationRequested()),会主动抛出 TaskCanceledException。因为你使用了 try...catch (OperationCanceledException) 代码块,你的代码已经安全地捕获了它。这就是为什么你的程序没有崩溃,并且成功打印了“========== 任务已取消 =========="。

关于你的异常设置(Exception Settings),取消勾选 TaskCanceledException 只是告诉 Visual Studio 在发生此异常时不要暂停(Break)你的程序。然而,Visual Studio 仍然通过在输出(Output)窗口中打印后台日志来管理异常,以便让你了解底层正在发生什么。

我的最佳建议是直接忽略这条信息。这是调试过程中的正常现象。它不会出现在你最终的 Release 发行版中,也完全不会影响你的应用程序稳定性。

如果你真的想在输出(Output)窗口中隐藏这段文字,你可以右键点击输出窗口内的任意位置,然后取消勾选“Exception Messages(异常消息)”。但我不建议这样做,因为这会隐藏所有的异常日志,可能会让你以后很难发现其他真正隐藏的 Bug。

希望我的解释能解答你的疑虑。如果您认为我的回答有帮助或提供了有用的信息,希望您能参考此指南来确认该答案。

谢谢。

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. AI answer

    2026-05-27T07:00:19.05+00:00

    TaskCanceledException is the expected exception type when a Task that observes a CancellationToken is canceled. In the posted code, cancellation is already handled correctly inside the task (catch (OperationCanceledException)), but the outer await of _runningTask in StartButton_Click is missing. Because StartButton_Click is async void, any TaskCanceledException that bubbles out of _runningTask (or from Task.Run) can appear as an unhandled exception in the synchronization context.

    To avoid the console output and keep cancellation fully controlled, use these patterns:

    1. Return Task from the event handler and await _runningTask

    Change the event handler signature and await the task so that cancellation is observed and handled:

    private async Task StartButton_Click(object sender, RoutedEventArgs e)
    {
        if (_runningTask != null && !_runningTask.IsCompleted)
        {
            AppendLog("已有任务正在运行,请先取消");
            return;
        }
    
        _cancellationTokenSource = new CancellationTokenSource();
        var token = _cancellationTokenSource.Token;
    
        StartButton.IsEnabled = false;
        CancelButton.IsEnabled = true;
        StatusText.Text = "任务运行中...";
        StatusText.Foreground = System.Windows.Media.Brushes.Orange;
        AppendLog("========== 开始执行任务 ==========");
    
        _runningTask = Task.Run(async () =>
        {
            try
            {
                for (int i = 1; i <= 10; i++)
                {
                    token.ThrowIfCancellationRequested();
                    await Task.Delay(500, token);
    
                    Dispatcher.Invoke(() =>
                    {
                        AppendLog($"步骤 {i}/10 完成");
                    });
                }
    
                Dispatcher.Invoke(() =>
                {
                    StatusText.Text = "任务完成";
                    StatusText.Foreground = System.Windows.Media.Brushes.Green;
                    AppendLog("========== 任务成功完成 ==========");
                });
            }
            catch (OperationCanceledException)
            {
                Dispatcher.Invoke(() =>
                {
                    StatusText.Text = "任务已取消";
                    StatusText.Foreground = System.Windows.Media.Brushes.Red;
                    AppendLog("========== 任务已取消 ==========");
                });
            }
            catch (Exception ex)
            {
                Dispatcher.Invoke(() =>
                {
                    StatusText.Text = "任务出错";
                    StatusText.Foreground = System.Windows.Media.Brushes.Red;
                    AppendLog($"任务执行出错: {ex.Message}");
                });
            }
            finally
            {
                Dispatcher.Invoke(() =>
                {
                    StartButton.IsEnabled = true;
                    CancelButton.IsEnabled = false;
                });
            }
        }, token);
    
        try
        {
            await _runningTask; // Observe cancellation here
        }
        catch (OperationCanceledException)
        {
            // Already handled inside; swallow if desired
        }
    }
    

    If the UI framework requires void for the event handler, keep async void but still await _runningTask inside the handler; the key is that the TaskCanceledException must be awaited and optionally caught.

    1. Do not pass the token to Task.Run if cancellation is handled inside

    Currently, Task.Run is called with token:

    _runningTask = Task.Run(async () => { ... }, token);
    

    This allows the task to be canceled before the delegate runs, causing Task.Run itself to throw TaskCanceledException. Since cancellation is already handled inside the delegate (ThrowIfCancellationRequested and Task.Delay(…, token)), remove the token from Task.Run:

    _runningTask = Task.Run(async () =>
    {
        // same body as before
    });
    

    Then only the inner code throws OperationCanceledException, which is caught and handled, and _runningTask will complete in the Canceled state without surfacing an unhandled TaskCanceledException.

    1. General guidance from task cancellation pattern

    The recommended pattern is:

    • Use CancellationTokenSource.Cancel to request cancellation.
    • Inside the task, periodically call token.ThrowIfCancellationRequested() or use APIs that accept the token (such as Task.Delay(…, token)).
    • Catch OperationCanceledException (or use ThrowIfCancellationRequested) so the task transitions to TaskStatus.Canceled instead of RanToCompletion.
    • Ensure callers await the task so that cancellation and exceptions are observed rather than treated as unhandled.

    This matches the documented pattern for task cancellation and async return types: methods that can throw should return Task/Task<TResult> and be awaited, not void.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.