【问题标题】:Using tasks getting "Object reference not set to an instance of an object"使用获取“对象引用未设置为对象实例”的任务
【发布时间】:2016-12-22 21:10:37
【问题描述】:

首先,我已经在许多 NullReferenceException 问题中搜索过 SO。 herehere

当我尝试调用 Task.WaitAll(tasks); 时收到错误消息“对象引用未设置为对象的实例”

我确信在尝试调用方法之前我正在初始化所有对象。以下是代码片段:

public IList<ResourceFreeBusyDto> GetResourceFreeBusy(int requesterId, int[] resourceIds, DateTime start, DateTime end)
    {
        IList<ResourceFreeBusyDto> result = new List<ResourceFreeBusyDto>();

        ValidateFreeBusyInputs(resourceIds, start, end);

        List<Task<IList<ResourceFreeBusyDto>>> tasks = new List<Task<IList<ResourceFreeBusyDto>>>();
        TimeSpan timeout = new TimeSpan(0,0,30); // 30 seconds          

        // Split resources to persons and meetingRooms
        List<int> personIds;
        List<int> meetingRoomIds;

        SplitResourceIds(resourceIds, out personIds, out meetingRoomIds);

        // Go online for persons
        if (personIds.Count > 0)
        {
            //result.AddRange(GetResourceFreeBusyOnline(requesterId, personIds.ToArray(), start, end)); // paralelizovat
            Task<IList<ResourceFreeBusyDto>> task = Task.Factory.StartNew(() => GetResourceFreeBusyOnline(requesterId, personIds.ToArray(), start, end));
            tasks.Add(task);
        }

        // Go online for meetingrooms if they are not cached in DB
        if (meetingRoomIds.Count > 0)
        {
            DateTime? lastModifiedMeetingRoomFreeBusy = new DateTime();

            lastModifiedMeetingRoomFreeBusy = freeBusyRepository.GetMinTimeStamp();

            if (lastModifiedMeetingRoomFreeBusy.Value.AddMinutes(1) < DateTime.UtcNow || lastModifiedMeetingRoomFreeBusy == null)
            {
                //result.AddRange(GetResourceFreeBusyOnline(requesterId, meetingRoomIds.ToArray(), start, end)); // mrIDs
                Task<IList<ResourceFreeBusyDto>> task = Task.Factory.StartNew(() => GetResourceFreeBusyOnline(requesterId, resourceIds, start, end));
                tasks.Add(task);
            }
            else
            {
                //result.AddRange(GetMeetingRoomsFreeBusyCached(requesterId, meetingRoomIds.ToArray(), start, end)); // mrIDs
                Task<IList<ResourceFreeBusyDto>> task = Task.Factory.StartNew(() => GetMeetingRoomsFreeBusyCached(requesterId, resourceIds, start, end));
                tasks.Add(task);
            }
        }

        bool status = false;

        try
        {
            var a = tasks.ToArray();
            Task.WaitAll(a);
            status = Task.WaitAll(tasks.ToArray(), timeout);
        }
        catch (Exception ex)
        {

            Log.Fatal(ex);
        }


        if (status == false)
        {
            throw new ApplicationException(
            string.Format("Timeout expired." +
            " The timeout period elapsed prior to completion of the asynchronous importing task executing" +
            " or the server is not responding. Try it later!"));
        }
        else
        {
            foreach (Task<IList<ResourceFreeBusyDto>> task in tasks)
            {
                result.AddRange(task.Result);
            }
        }

        return result;

    }

注意

var a = tasks.ToArray();
Task.WaitAll(a);

是抛出异常的测试。 var a = tasks.ToArray(); 正在顺利通过。

这里抛出异常:

Task.WaitAll(a); 

这里

status = Task.WaitAll(tasks.ToArray(), timeout);

你能解释一下发生了什么吗?在调试的过程中可以看到tasks被初始化了。

附:注释行例如:result.AddRange(GetResourceFreeBusyOnline(requesterId, meetingRoomIds.ToArray(), start, end)); 是在单线程中调用方法的行,它正在无错误地传递并返回预期的结果。

堆栈

2016-12-22 13:24:18,844 [9] FATAL Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks - System.AggregateException: One or more errors occurred. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSimpleSessionStorage()
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSessionForKey(String factoryKey)
   at SharpArch.NHibernate.NHibernateSession.CurrentFor(String factoryKey)
   at SharpArch.NHibernate.NHibernateRepositoryWithTypedId`2.get_Session()
   at SharpArch.NHibernate.LinqRepositoryWithTypedId`2.FindAll()
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.GetResourceFreeBusyOnline(Int32 requesterId, Int32[] resourceIds, DateTime start, DateTime end) in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 238
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.<>c__DisplayClass7_0.<GetResourceFreeBusy>b__0() in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 83
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.GetResourceFreeBusy(Int32 requesterId, Int32[] resourceIds, DateTime start, DateTime end) in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 113
---> (Inner Exception #0) System.NullReferenceException: Object reference not set to an instance of an object.
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSimpleSessionStorage()
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSessionForKey(String factoryKey)
   at SharpArch.NHibernate.NHibernateSession.CurrentFor(String factoryKey)
   at SharpArch.NHibernate.NHibernateRepositoryWithTypedId`2.get_Session()
   at SharpArch.NHibernate.LinqRepositoryWithTypedId`2.FindAll()
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.GetResourceFreeBusyOnline(Int32 requesterId, Int32[] resourceIds, DateTime start, DateTime end) in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 238
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.<>c__DisplayClass7_0.<GetResourceFreeBusy>b__0() in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 83
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()<---

---> (Inner Exception #1) System.NullReferenceException: Object reference not set to an instance of an object.
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSimpleSessionStorage()
   at SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSessionForKey(String factoryKey)
   at SharpArch.NHibernate.NHibernateSession.CurrentFor(String factoryKey)
   at SharpArch.NHibernate.NHibernateRepositoryWithTypedId`2.get_Session()
   at SharpArch.NHibernate.LinqRepositoryWithTypedId`2.FindAll(ILinqSpecification`1 specification)
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.GetMeetingRoomsFreeBusyCached(Int32 requesterId, Int32[] meetingRoomIds, DateTime start, DateTime end) in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 196
   at Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.<>c__DisplayClass7_0.<GetResourceFreeBusy>b__2() in C:\_Hg\Tieto.MyMeetings\source\server\Tieto.MyMeetings.Tasks\FreeBusy\FreeBusyTasks.cs:line 103
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()<---

【问题讨论】:

  • 任务不会生成空值。返回 null 或将变量设置为 null 会生成 null。与往常一样,调试代码,确保初始化所有变量等
  • 至少发布 full 异常,包括它的调用堆栈。 WaitAll 几乎肯定会重新抛出由其中一项任务引发的异常。事实上,这个问题可能应该作为重复关闭。
  • 问题已编辑(添加调用堆栈)
  • 用 P.S. 编辑的问题。在注释部分
  • 调用堆栈指向 GetMeetingRoomsFreeBusyCached 中的问题。你试过调试吗?

标签: c# task nullreferenceexception


【解决方案1】:

Task.WaitAll 不会抛出此异常。它重新抛出其任务之一引发的异常。如果没有完整的异常和调用堆栈(由Exception.ToString() 返回),则无法确定,但标准指南也适用于此处 - 不知何故,使用了未初始化的变量或参数。

例如:

List<int> personIds;
List<int> meetingRoomIds;

SplitResourceIds(resourceIds, out personIds, out meetingRoomIds);

可以将personIdsmeetingRoomIds 都设置为null。这意味着

var task = Task.Factory.StartNew(() => GetResourceFreeBusyOnline(requesterId,
                                                                 personIds.ToArray(), 
                                                                 start, end));

会在任务中抛出一个异常,在调用Task.WaitAll 时会重新引发该异常。

要解决此问题,只需遵循重复问题中的建议即可。检查参数是否为空,调试代码,检查异常的完整调用堆栈。 Task.WaitAll 将抛出一个 AggregateException,它在其 InnerExceptions 属性中包含所有底层异常。

至少您应该捕获AggregateException 以分别处理和记录任务中引发的异常。

最后,使用 async/awaitTask.Runawait Task.WhenAll 代替 StartNew 和 Task.WaitAll。 await 解开 AggregateException 并引发底层异常,这使得调试更加容易。

更新

从调用堆栈看来Tieto.MyMeetings.Tasks.FreeBusy.FreeBusyTasks.GetMeetingRoomsFreeBusyCached 调用SharpArch.NHibernate.LinqRepositoryWithTypedId'2.FindAll 并传递一个空参数或包含空的项目列表。这些值可能用于创建会话密钥,因为在引发异常之前会调用 SharpArch.NHibernate.Web.Mvc.WebSessionStorage.GetSessionForKey

找到确切的问题需要调试代码并进入GetMeetingRoomsFreeBusyCached

【讨论】:

  • 谢谢,您向我展示了正确的路径以及在哪里可以找到问题。我们将 NHIbernate 会话创建为尚未准备好用于多线程的会话。所以我不得不把所有从 DB 中选择的代码放到 Tasks 之外。
  • 您应该认真考虑迁移到 EF 或像 Dapper 这样的 microORM。 NHibernate 已经落后,因为异步操作需要完全重写。您不能使用额外的线程来伪造异步数据库操作 - ADO.NET 异步操作不使用线程,它们使用操作系统的 IO 完成端口来工作而不会阻塞任何线程。至少确保你有latest version
猜你喜欢
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多