我在写一个事件调度器的回调方法时,碰到了一个奇怪的问题:
private void TimeCallBackFunction()
{
Action task;
while ((task = PopWithPushNext(DateTime.Now)) != null)
{
ThreadPool.QueueUserWorkItem(useless => task.Invoke(), null); // throw an System.NullReferenceException, task是null
}
Thread.Sleep(1000);
}
我觉得奇怪的是明明在上面已经判断过task是否为null了,但是还是会报这个异常(异常并不总是出现),task是一个局部变量,外围没有申明过同名的静态变量;这个是个单线程方法。
希望有遇到过这类问题或者清除原因的大佬可以帮我解答一下这个疑惑。
修改之后的代码
private void TimeCallBackFunction()
{
Action action;
while ((action = PopWithPushNext(DateTime.Now)) != null)
{
Action task = new Action(action);//重新创建一个action的拷贝
ThreadPool.QueueUserWorkItem(useless => task());
}
Thread.Sleep(1000);
}
这样就可以解决这个问题了。
试试把 Action 改为 Task ,Task task;
我觉得有没有可能对于ThreadPool来说我的task变量不是线程安全的,后续当task的值被修改为null的时候,加入到线程池的时候报了这个问题。因为我while循环的时候task是会变为null。
@一棵猪油草: 建议提供重现这个问题的示例代码
@一棵猪油草: 你这个只是解决了 Access to modified closure
问题,直接通过 var action1 = action 就行,这两者是不同的行为。
是task==null,还是 task的方法里面有null exception
System.NullReferenceException:“未将对象引用设置到对象的实例。”
task 是 null。
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
TimeCallbackFunc();
}
static void TimeCallbackFunc()
{
Action task;
while ((task = PopWithPushNext(DateTime.Now)) != null)
{
ThreadPool.QueueUserWorkItem(useless => task.Invoke(), null);
}
}
static Action PopWithPushNext(object value)
{
return () => { Console.WriteLine("Current Time is ={0}", value); };
}
}
我简单写了一个Sample,是没有问题的。建议提供一下你的 回调函数 PopWithPushNext
QueueUserWork 接收的委托定义为
public delegate void WaitCallback(object state);
ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Hello,This is my function= {0}", state), DateTime.Now);
拆分开模拟一下,看哪里出问题。
@BUTTERAPPLE: 感谢回复,我已经找到问题了,原因是相对于ThreadPool来说task变量并不是一个线程安全的值,它可能变为null的时候再添加到线程池的时候抛出了这个异常。感谢。
Action task当然是null了,你这句代码只是定义了一个task并没有实例化。
你怎么确定
– dudu 5年前task是null
的?@dudu: 因为编译器抛出的异常说task是null,抛出异常后我查询了task的值,确实也是为null。
– 一棵猪油草 5年前@一棵猪油草: 修改后的代码还有一个地方不同,没有使用
– dudu 5年前Invoke