【问题标题】:TPL Task ContinueWith Result and Exception result in Unhandled ExceptionTPL 任务 ContinueWith Result 和 Exception 导致 Unhandled Exception
【发布时间】:2013-10-04 06:52:31
【问题描述】:

我使用 ContinueWith 运行一个任务,该任务具有双重职责:如果任务成功完成则处理结果,或者如果确实发生错误则处理任何异常。但是下面的代码不会正确处理任何异常,它会注册为未处理并关闭程序(为了发布而有所缩短,因此可能并不完美):

void _SqlServerDatabaseListLoader()
{
    _ClearSqlHolders(true, false);
    _SqlConnectionStringHolder.Database = "master";
    if (_SqlConnectionStringHolder.IsComplete)
    {
        //Could time out put on its own thread with a continuation back on the UI thread for the popup
        _TaskCanceller = new CancellationTokenSource();
        _TaskLoader = Task.Factory.StartNew(() =>
        {
            IsLoadingSqlServerDatabaseList = true;

            using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder))
            {
                // Open connection
                con.Open(); //If this cause an error (say bad password) the whole thing bombs

                //create a linq connection and get the list of database names
                DataContext dc = new DataContext(con);
                return new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection());
            }
        }).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Result, antecendant.Exception),
            _TaskCanceller.Token,
            TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }
}

void _SqlServerDatabaseListLoaderComplete(ObservableCollection<string> DatabaseList, AggregateException ae)
{
    //Just show the first error
    if (ae != null)
        ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error");

    if(DatabaseList != null)
        SqlServerDatabaseList = DatabaseList

    //Set the running indicator
    _TaskLoader = null;
    _TaskCanceller = null;
    IsLoadingSqlServerDatabaseList = false;
}

我正在使用 TaskContinuationOptions.None 来解决这个问题,我认为这是正确的。这在上面的类继承自的基类中声明:

protected Task _TaskLoader;
protected CancellationTokenSource _TaskCanceller;

如果我在不会导致错误的场景下运行,一切都会正常,我会得到我的数据库列表。但如果出现错误,比如有人为此 SQL Server 登录凭据提供了错误密码,则不会处理该错误。

但是,如果我删除了传递 Result 参数的选项,一切都会正常运行,并且会捕获异常:

void _SqlServerDatabaseListLoader()
{
    _ClearSqlHolders(true, false);
    _SqlConnectionStringHolder.Database = "master";
    if (_SqlConnectionStringHolder.IsComplete)
    {
        //Could time out put on its own thread with a continuation back on the UI thread for the popup
        _TaskCanceller = new CancellationTokenSource();
        _TaskLoader = Task.Factory.StartNew(() =>
        {
            IsLoadingSqlServerDatabaseList = true;

            using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder))
            {
                // Open connection
                con.Open();

                //create a linq connection and get the list of database names
                DataContext dc = new DataContext(con);

                //HAVE TO SET IN THE THEAD AND NOT RETURN A RESULT
                SqlServerDatabaseList = new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection());
            }
        }).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Exception),
            _TaskCanceller.Token,
            TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }
}

void _SqlServerDatabaseListLoaderComplete(AggregateException ae)
{
    //Just show the first error
    if (ae != null)
        ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error");

    //Set the running indicator
    _TaskLoader = null;
    _TaskCanceller = null;
    IsLoadingSqlServerDatabaseList = false;
}

我假设我没有完全理解 TPL 应该如何工作。我尝试创建一个以上的 ContinueWith,但这似乎并没有什么不同。感谢您的帮助。

【问题讨论】:

    标签: .net wpf exception task-parallel-library


    【解决方案1】:

    问题是获取Task&lt;T&gt;.Result 会引发AggregateException此时,这发生在之前你可以真正抓住异常,并阻止你的方法被调用。

    一种选择是使用两种延续 - 一种用于发生异常时,一种用于未发生异常时:

        _TaskLoader = Task.Factory.StartNew(() =>
        {
            IsLoadingSqlServerDatabaseList = true;
    
            using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder))
            {
                // Open connection
                con.Open();
    
                //create a linq connection and get the list of database names
                DataContext dc = new DataContext(con);
    
                //HAVE TO SET IN THE THEAD AND NOT RETURN A RESULT
                SqlServerDatabaseList = new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection());
            }
        });
    
        // This method is called if you get an exception, and processes it
        _TaskLoader.ContinueWith(antecendant => _SqlServerDatabaseListLoaderFaulted(antecendant.Exception),
            _TaskCanceller.Token,
            TaskContinuationOptions.OnlyOnFaulted,
            TaskScheduler.FromCurrentSynchronizationContext());
    
        // This method is called if you don't get an exception, and can safely use the result
        _TaskLoader.ContinueWith(antecendant => _SqlServerDatabaseListLoaderCompleted(antecendant.Result),
            _TaskCanceller.Token,
            TaskContinuationOptions.NotOnFaulted,
            TaskScheduler.FromCurrentSynchronizationContext());
    

    另一种选择是将Task&lt;T&gt; 本身(antecendant)作为参数传递给方法。然后可以检查task.Exception,如果不为null,则显示异常,否则,处理结果。

    【讨论】:

    • 谢谢里德。很有意思。我实际上尝试过,但无法让它去。感谢有关通过整个任务但同样的事情的想法。它不喜欢“.Result”——它给出了一个不存在的设计时错误(两种情况)。真奇怪。如果我使用_TaskLoader,它会给我一个签名错误,说它不能将任务转换为任务>。如果我设置“受保护的 Task> _TaskLoader”,我仍然会收到错误消息。但是,如果我执行“var x = Task>.Factory.StartNew(() =>....”,它会起作用。
    • @Ernie 之前,您将_TaskLoader 设置为您的continuation,而不是原始任务。这可能就是您遇到类型不匹配的原因
    • 啊啊啊啊……原来是里德!我认为这一直是我的问题,包括当我在发布之前尝试用两个 ContinueWith 打破它时。你的两个建议都奏效了。感谢所有帮助!
    猜你喜欢
    • 1970-01-01
    • 2018-02-23
    • 2022-12-01
    • 2021-06-04
    • 2012-11-19
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2019-07-10
    相关资源
    最近更新 更多