Lambda遇到UnobservedTaskException-无法访问已处置的对象

时间:2018-09-10 01:34:13

标签: c# async-await aws-lambda amazon-cloudwatch objectdisposedexception

我一直在AWS Lambda的CloudWatch日志中注意到此异常。

一切似乎都已得到处理,因此我认为这是Lambda完成执行后创建的AWS代码中的一个异常(与我编写的内容相反)。

从功能上来说,它一直有效,但是我一直忽略它,但是我担心可能存在一些我没有注意到的问题。


Lambda通过以下方式遇到UnobservedTaskException 'TaskScheduler.UnobservedTaskException'事件:

{
    "errorType": "AggregateException",
    "errorMessage": "A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. (Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.)",
    "cause": {
        "errorType": "ObjectDisposedException",
        "errorMessage": "Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.",
        "stackTrace": [
            "at System.Net.Sockets.UdpClient.EndReceive(IAsyncResult asyncResult, IPEndPoint& remoteEP)",
            "at System.Net.Sockets.UdpClient.<>c.<ReceiveAsync>b__56_1(IAsyncResult asyncResult)",
            "at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)"
        ]
    },
    "causes": [ {
        "errorType": "ObjectDisposedException",
        "errorMessage": "Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.",
        "stackTrace": [
            "at System.Net.Sockets.UdpClient.EndReceive(IAsyncResult asyncResult, IPEndPoint& remoteEP)",
            "at System.Net.Sockets.UdpClient.<>c.<ReceiveAsync>b__56_1(IAsyncResult asyncResult)",
            "at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)"
        ]
    }]
}

lambda代码非常简单:它使用SNS messages将这些Dapper添加到SQL数据库中。

我认为在Fucntion处理程序中我的操作async可能存在一些问题。有什么想法吗?

public class Function
{
    private static string _connectionString;

    public async Task<IEnumerable<InsertSnsResult>> FunctionHandler(SNSEvent @event, ILambdaContext context)
    {
        try
        {
            context.Logger.LogLine("Adding SNS Messages");
            _connectionString = _connectionString ?? await DecryptHelper.DecryptEnvironmentVariableAsync("ConnectionString").ConfigureAwait(false);
            var handler = new AddSnsMessageHandler(new SnsMessagesRepository(_connectionString, context.Logger));
            return await handler.AddSnsEvents(@event).ConfigureAwait(false);
        }
        catch (Exception e)
        {
            context.Logger.LogLine(e.Message);
            throw;
        }
        finally
        {
            context.Logger.LogLine("Finished SNS Adding Messages");
        }
    }
}

[编辑]

请在此处明确说明,此异常不会被捕获在try / catch块中。如果这样做的话,就不会是UnobservedTaskException。这就是为什么我很难找到问题的根源。

这是存储库代码

public async Task<List<InsertSnsResult>> InsertSnsMessages(IEnumerable<SnsEvent> records)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        await connection.OpenAsync().ConfigureAwait(false);

        var results = new List<InsertSnsResult>();
        foreach (var record in records)
        {
            try
            {
                await connection.ExecuteAsync(InsertEventCommand, record).ConfigureAwait(false);
                results.Add(new InsertSnsResult(record.CorrelationId, true));
            }
            catch (Exception ex)
            {
                _logger.LogLine($"InsertSns failed for {record.Id}. {ex.Message}");
                results.Add(new InsertSnsResult(record.CorrelationId, false));
            }
        }

        return results;
    }            
}

2 个答案:

答案 0 :(得分:3)

日志消息很简单,并说明了正在发生的事情:

  • 您有一个异步任务
  • 该异步任务正在访问已被处置的对象,这可能是因为您的工作流程中存在某些竞争条件,从而异步工作流程中的对象与需要它的工作流程的另一部分无序地处置。 这意味着在此工作流程中有严重的问题
  • 永远不会等待异步任务,要么与await异步,要么与Result或Wait同步(不要这样做!)。这意味着永远不会采用异常延续路径,并且任务在收集时会注意到这一点。 同样,如果您的任务永不等待结果,则可能会严重破坏工作流程。将此事实与上一点的事实结合起来:我们现在有两个证据,相互补充,证明此工作流程中有严重中断的事情,并且涉及一个尚未等待的任务应该是为了确保排序约束。
  • 因此,您的终结器线程上出现异常,这真的很糟糕。
  

从功能上讲,我一直忽略它

我曾经听说,当一家工厂起火燃烧到地面时,平均而言,人们会忽略或禁用七种不同的安全系统。摆脱这种认为它起作用的习惯,因此它必须是安全的。也许没什么,但我认为这些消息表明存在严重问题,除非我有其他证据。

答案 1 :(得分:0)

我也遇到了一个第三方库导致错误的地方。我想将其记录在CloudWatch之外。为了防止Lambda记录这些事件,我能够进行一些邪恶的反思来重置事件处理程序。

以下是您自己执行此操作的代码。请注意,这是邪恶代码。它很脆弱,并且在CLR中更改代码时,即使编译器进行了优化(最近发生的情况),也会破坏代码。但是,这是我选择退出Lambda提供的此功能的唯一方法

private void ReplaceLambdaDefaultUnobservedTaskException()
{
    try
    {
        var bindingFlags = BindingFlags.NonPublic | BindingFlags.Static;
        Type type = typeof(TaskScheduler);

        var field = type.GetField("_unobservedTaskException", bindingFlags);

        if (field == null)
        {
            field = type.GetField("UnobservedTaskException", bindingFlags);
        }

        var handler = new EventHandler<UnobservedTaskExceptionEventArgs>(TaskSchedulerOnUnobservedTaskException);
        field.SetValue(null, handler);
    }
    catch (Exception ex)
    {
        logger.Warning(ex, "Unable to do evil reflection.");
    }

    TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
}

private void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
    e.SetObserved();
    logger.Error(e.Exception, "Lambda threw an UnobservedTaskException");
}