ThreadPool QueueUserWorkItem 抛出 NullReferenceException
ThreadPool QueueUserWorkItem Throws NullReferenceException
这是一个有点奇怪的问题,因为我无法重现它。我有两个能够重现此问题的客户向我发送了 WER 日志。
我有一个 C# 应用程序,它对工作项进行排队并在后台执行它们。在极少数机器中,在尝试执行工作线程的第一行时,它会立即抛出 NPE。
Ausnahmeinformationen: System.NullReferenceException
bei magicsim.SimcPreloaderData+<>c__DisplayClass16_0.<LoadArmoryData>b__0(System.Object)
bei System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)
bei System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
bei System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
bei System.Threading.ThreadPoolWorkQueue.Dispatch()
bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
WER 的信息也很有趣。
Problemsignatur:
P1: magicsim.exe
P2: 2.2.0.2
P3: 5b7df04c
P4: magicsim
P5: 2.2.0.2
P6: 5b7df04c
P7: 29b
P8: fb
P9: System.NullReferenceException
P10:
所以这是在方法 29b
中,试图命中 IL 行 fb
。
使用 ILDASM 我可以验证此问题签名的位置是否与堆栈跟踪 (SimcPreloaderData) 匹配
Method #2 (0600029b)
-------------------------------------------------------
MethodName: <LoadArmoryData>b__0 (0600029B)
Flags : [Assem] [HideBySig] [ReuseSlot] (00000083)
RVA : 0x0000d338
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
1 Arguments
Argument #1: Object
1 Parameters
(1) ParamToken : (0800020f) Name : _ flags: [none] (00000000)
在ILSpy中,我可以看到上述方法的结构如下:
.method public hidebysig
instance void LoadArmoryData (
string simcString
) cil managed
{
// Method begins at RVA 0x7b4d
// Code size 48 (0x30)
.maxstack 8
IL_0000: newobj instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::.ctor()
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stfld class magicsim.SimcPreloaderData magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<>4__this'
IL_000c: dup
IL_000d: ldarg.1
IL_000e: stfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
IL_0013: ldarg.0
IL_0014: ldstr "Acquiring SimC Executable"
IL_0019: call instance void magicsim.SimcPreloaderData::set_Label(string)
IL_001e: ldftn instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<LoadArmoryData>b__0'(object)
IL_0024: newobj instance void [mscorlib]System.Threading.WaitCallback::.ctor(object, native int)
IL_0029: call bool [mscorlib]System.Threading.ThreadPool::QueueUserWorkItem(class [mscorlib]System.Threading.WaitCallback)
IL_002e: pop
IL_002f: ret
} // end of method SimcPreloaderData::LoadArmoryData
显然没有要执行的 IL_00fb 命令,因此 NPE 和缺乏对发生在 LoadArmoryData 中的具体位置的引用(除了事实就是如此)。从上面我唯一可以推测的是,由于某种原因,多线程正在出错并试图访问不存在的指令。
我不知道为什么会发生这种情况,并排除了防病毒和权限干扰。
编辑:为了进一步参考,完整的方法。
public void LoadArmoryData(String simcString)
{
SimC simc;
Label = "Acquiring SimC Executable";
ThreadPool.QueueUserWorkItem((_) =>
{
try
{
simc = SimCManager.AcquireSimC();
}
catch (Exception e)
{
MessageBox.Show("Could not acquire SimC because of an Exception: " + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
return;
}
Label = "Generating SimC Profile";
Regex nameRegex = new Regex("(warrior|paladin|hunter|rogue|priest|deathknight|shaman|mage|warlock|monk|druid|demonhunter)+=\"?([^\r\n\"]+)\"?");
String name = nameRegex.Match(simcString).Groups[2].Value;
String text = simcString + "\nsave=./characters/" + name + ".simc";
try
{
if (!Directory.Exists("characters"))
{
Directory.CreateDirectory("characters");
}
if (File.Exists("characters/" + name))
{
File.Delete("characters/" + name);
}
File.WriteAllText("characters/" + name + ".simc", text);
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show("Could not write character profile due to a permissions issue. Please retry, running as Administrator." + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
return;
}
if (simc.RunSim("characters/" + name + ".simc"))
{
Label = "SimC Profile Generated";
charName = name;
App.Current.Dispatcher.Invoke(() =>
{
PreloadingComplete(this, new EventArgs());
});
}
else
{
Label = "Failed to Generate Profile";
MessageBox.Show("Failed to generate profile. Please check your input and try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
}
});
}
编辑 2(为了完整性):
其实29b指的是b__0,不是LoadArmoryData。
如果您查看 ILSpy 中的正确方法,您会看到以下命令序列,这暗示了明确的问题和潜在的解决方案。
// string contents = simcString + "\nsave=./characters/" + value + ".simc";
IL_00ce: ldarg.0
IL_00cf: ldfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
IL_00d4: ldstr "\nsave=./characters/"
IL_00d9: ldloc.0
IL_00da: ldstr ".simc"
IL_00df: call string [mscorlib]System.String::Concat(string, string, string, string)
IL_00e4: stloc.1
// File.WriteAllText("characters/" + value + ".simc", contents);
IL_00e5: ldstr "characters/"
IL_00ea: ldloc.0
IL_00eb: ldstr ".simc"
IL_00f0: call string [mscorlib]System.String::Concat(string, string, string)
IL_00f5: ldloc.1
IL_00f6: call void [mscorlib]System.IO.File::WriteAllText(string, string)
// if (simc.RunSim("characters/" + value + ".simc"))
IL_00fb: ldarg.0
IL_00fc: ldfld class magicsim.SimC magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simc
IL_0101: ldstr "characters/"
IL_0106: ldloc.0
IL_0107: ldstr ".simc"
IL_010c: call string [mscorlib]System.String::Concat(string, string, string)
IL_0111: callvirt instance bool magicsim.SimC::RunSim(string)
// (no C# code)
IL_0116: brfalse.s IL_0163
我认为问题中的分析有缺陷(例如异常来自 <LoadArmoryData>b__0(System.Object)
,它表示 LoadArmoryData 中的 lambda 表达式,其签名需要一个对象参数;但您正在查看 LoadArmoryData 的 IL ,这是一种方法,其签名需要一个字符串参数)。
这只是一个简单的 NullReferenceException,可能来自 nameRegex.Match(simcString).Groups[2].Value;
当 Regex 不匹配时。
这是一个有点奇怪的问题,因为我无法重现它。我有两个能够重现此问题的客户向我发送了 WER 日志。
我有一个 C# 应用程序,它对工作项进行排队并在后台执行它们。在极少数机器中,在尝试执行工作线程的第一行时,它会立即抛出 NPE。
Ausnahmeinformationen: System.NullReferenceException
bei magicsim.SimcPreloaderData+<>c__DisplayClass16_0.<LoadArmoryData>b__0(System.Object)
bei System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)
bei System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
bei System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
bei System.Threading.ThreadPoolWorkQueue.Dispatch()
bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
WER 的信息也很有趣。
Problemsignatur:
P1: magicsim.exe
P2: 2.2.0.2
P3: 5b7df04c
P4: magicsim
P5: 2.2.0.2
P6: 5b7df04c
P7: 29b
P8: fb
P9: System.NullReferenceException
P10:
所以这是在方法 29b
中,试图命中 IL 行 fb
。
使用 ILDASM 我可以验证此问题签名的位置是否与堆栈跟踪 (SimcPreloaderData) 匹配
Method #2 (0600029b)
-------------------------------------------------------
MethodName: <LoadArmoryData>b__0 (0600029B)
Flags : [Assem] [HideBySig] [ReuseSlot] (00000083)
RVA : 0x0000d338
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
1 Arguments
Argument #1: Object
1 Parameters
(1) ParamToken : (0800020f) Name : _ flags: [none] (00000000)
在ILSpy中,我可以看到上述方法的结构如下:
.method public hidebysig
instance void LoadArmoryData (
string simcString
) cil managed
{
// Method begins at RVA 0x7b4d
// Code size 48 (0x30)
.maxstack 8
IL_0000: newobj instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::.ctor()
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stfld class magicsim.SimcPreloaderData magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<>4__this'
IL_000c: dup
IL_000d: ldarg.1
IL_000e: stfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
IL_0013: ldarg.0
IL_0014: ldstr "Acquiring SimC Executable"
IL_0019: call instance void magicsim.SimcPreloaderData::set_Label(string)
IL_001e: ldftn instance void magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::'<LoadArmoryData>b__0'(object)
IL_0024: newobj instance void [mscorlib]System.Threading.WaitCallback::.ctor(object, native int)
IL_0029: call bool [mscorlib]System.Threading.ThreadPool::QueueUserWorkItem(class [mscorlib]System.Threading.WaitCallback)
IL_002e: pop
IL_002f: ret
} // end of method SimcPreloaderData::LoadArmoryData
显然没有要执行的 IL_00fb 命令,因此 NPE 和缺乏对发生在 LoadArmoryData 中的具体位置的引用(除了事实就是如此)。从上面我唯一可以推测的是,由于某种原因,多线程正在出错并试图访问不存在的指令。
我不知道为什么会发生这种情况,并排除了防病毒和权限干扰。
编辑:为了进一步参考,完整的方法。
public void LoadArmoryData(String simcString)
{
SimC simc;
Label = "Acquiring SimC Executable";
ThreadPool.QueueUserWorkItem((_) =>
{
try
{
simc = SimCManager.AcquireSimC();
}
catch (Exception e)
{
MessageBox.Show("Could not acquire SimC because of an Exception: " + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
return;
}
Label = "Generating SimC Profile";
Regex nameRegex = new Regex("(warrior|paladin|hunter|rogue|priest|deathknight|shaman|mage|warlock|monk|druid|demonhunter)+=\"?([^\r\n\"]+)\"?");
String name = nameRegex.Match(simcString).Groups[2].Value;
String text = simcString + "\nsave=./characters/" + name + ".simc";
try
{
if (!Directory.Exists("characters"))
{
Directory.CreateDirectory("characters");
}
if (File.Exists("characters/" + name))
{
File.Delete("characters/" + name);
}
File.WriteAllText("characters/" + name + ".simc", text);
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show("Could not write character profile due to a permissions issue. Please retry, running as Administrator." + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
return;
}
if (simc.RunSim("characters/" + name + ".simc"))
{
Label = "SimC Profile Generated";
charName = name;
App.Current.Dispatcher.Invoke(() =>
{
PreloadingComplete(this, new EventArgs());
});
}
else
{
Label = "Failed to Generate Profile";
MessageBox.Show("Failed to generate profile. Please check your input and try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Dispatcher.Invoke(() =>
{
PreloadingFailed(this, new EventArgs());
});
}
});
}
编辑 2(为了完整性):
其实29b指的是b__0,不是LoadArmoryData。
如果您查看 ILSpy 中的正确方法,您会看到以下命令序列,这暗示了明确的问题和潜在的解决方案。
// string contents = simcString + "\nsave=./characters/" + value + ".simc";
IL_00ce: ldarg.0
IL_00cf: ldfld string magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simcString
IL_00d4: ldstr "\nsave=./characters/"
IL_00d9: ldloc.0
IL_00da: ldstr ".simc"
IL_00df: call string [mscorlib]System.String::Concat(string, string, string, string)
IL_00e4: stloc.1
// File.WriteAllText("characters/" + value + ".simc", contents);
IL_00e5: ldstr "characters/"
IL_00ea: ldloc.0
IL_00eb: ldstr ".simc"
IL_00f0: call string [mscorlib]System.String::Concat(string, string, string)
IL_00f5: ldloc.1
IL_00f6: call void [mscorlib]System.IO.File::WriteAllText(string, string)
// if (simc.RunSim("characters/" + value + ".simc"))
IL_00fb: ldarg.0
IL_00fc: ldfld class magicsim.SimC magicsim.SimcPreloaderData/'<>c__DisplayClass16_0'::simc
IL_0101: ldstr "characters/"
IL_0106: ldloc.0
IL_0107: ldstr ".simc"
IL_010c: call string [mscorlib]System.String::Concat(string, string, string)
IL_0111: callvirt instance bool magicsim.SimC::RunSim(string)
// (no C# code)
IL_0116: brfalse.s IL_0163
我认为问题中的分析有缺陷(例如异常来自 <LoadArmoryData>b__0(System.Object)
,它表示 LoadArmoryData 中的 lambda 表达式,其签名需要一个对象参数;但您正在查看 LoadArmoryData 的 IL ,这是一种方法,其签名需要一个字符串参数)。
这只是一个简单的 NullReferenceException,可能来自 nameRegex.Match(simcString).Groups[2].Value;
当 Regex 不匹配时。