使用任务获取 "Object reference not set to an instance of an object"

Using tasks getting "Object reference not set to an instance of an object"

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

当我尝试调用 Task.WaitAll(tasks);

时出现错误 "Object reference not set to an instance of an object"

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

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 已初始化。

P.S。例如,注释行: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()<---

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

例如:

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

SplitResourceIds(resourceIds, out personIds, out meetingRoomIds);

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

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

将在任务中抛出异常,当调用 Task.WaitAll 时将重新引发。

要解决这个问题,只需按照重复问题中的建议进行操作即可。检查参数是否为空,调试代码,检查异常的完整调用堆栈。 Task.WaitAll 将抛出 AggregateException which contains all underlying exceptions in its InnerExceptions 属性。

至少你应该捕获 AggregateException 来单独处理和记录任务中引发的异常。

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

更新

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

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