【发布时间】:2021-06-19 05:10:32
【问题描述】:
我有一个如图所示的类设置 - 我知道长时间运行的任务会更好,但这是非常精简的原始状态。我已经删除了任何似乎对问题没有必要的内容。
TLDR:
- 代码循环运行,列出的条件(1/2/3)可以独立执行
- 日志显示
condition3仅在currentParentRun.ChildRuns.Count> 0 时才会执行 - 错误被抛出并被捕获,表明(连同其他日志)在 BeginInvoke 的调用函数中,计数 >0 的
currentParentRun.ChildRuns[currentParentRun.ChildRuns.Count - 1]以某种方式超出了范围。
// Class created and owned by UI
public class DataProcessor
{
private BackgroundWorker worker = new BackgroundWorker();
private ParentRun currentParentRun = null;
public DataProcessor()
{
// register background worker events
worker.WorkerSupportsCancellation = false;
worker.WorkerReportsProgress = false;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (process)
{
try
{
// Create a new parent
if (condition1)
{
currentParentRun = new ParentRun();
}
// Create a new child (parent will always be not null here)
if (condition2)
{
ChildRun childRun = new ChildRun();
currentParentRun.ChildRuns.Add(childRun);
}
// Call the UI and update
if (condition3)
{
ShowFinishedChildUI();
}
System.Threading.Thread.Sleep(loopDelayProcessor);
}
catch (Exception ex)
{
}
}
}
public void ShowFinishedChildUI()
{
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)(() => ShowFinishedChildUIDelegate()));
}
public void ShowFinishedChildUIDelegate()
{
// Parent is the UI being updated
parent.ShowFinishedChild(currentParentRun.ParentRunID,
currentParentRun.ChildRuns[currentParentRun.ChildRuns.Count - 1].ChildRunID);
resetParentDisplay = false;
}
}
这里,“父”是拥有的 UI 控件,我们使用 BeginInvoke 发送调用 UI 函数来更新。
每 20-30k 个新的“父”创建偶尔会发生什么,而在处理过程中,Index was out of range. Must be non-negative and less than the size of the collection. 的 ShowFinishedChildUIDelegate 函数出现错误据我所知,它是 总是至少非零,这让我认为在同一行代码之间的某个地方,孩子的数量在之间变化
currentParentRun.ChildRuns[...]
和
currentParentRun.ChildRuns.Count - 1
是否有可能如果在委托处理时主代码块中的condition2 得到满足,那么同一行代码可以有不同的值,其中子代的数量大于类的数量列表?这是我的最佳猜测,但我不确定如何验证。
谢谢。
更详细的错误:
2021-03-19 12:00:12.8174|ERROR|GeneralLogger|UnhandledException StackTrace : at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.get_Item(Int32 index)
at QASystem.Classes.DataProcessor.ShowFinishedChildUIDelegate() in D:\..\QASystem\QASystem\Classes\DataProcessor.cs:line 756
at QASystem.Classes.DataProcessor.<ShowFinishedChildUI>b__76_0() in D:\..\QASystem\QASystem\Classes\DataProcessor.cs:line 751
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at QASystem.App.Main()
【问题讨论】:
-
标记您的 GUI:WPF、WinForms 等?
-
避免空的 Try-Catches。它在向你隐瞒问题。在不知道“条件#”是什么意思的情况下,您的代码可能使
condition2为假但condition3为真,因此它可能会抛出该异常。但我们没有看到该代码。 -
我删除了 try catch 以使代码简洁,它记录了超出范围的错误。
Condition3可以在没有condition2的情况下执行 - 这是有效的 - 问题是当.Count> 0 时,这 (currentParentRun.ChildRuns[currentParentRun.ChildRuns.Count - 1]) 会给我一个索引超出范围异常,这似乎是不可能的。 -
作为旁注,
BackgroundWorker类具有事件ProgressChanged和RunWorkerCompleted,您应该使用它们在处理期间和之后与 UI 进行交互。使用Dispatcher与UI 通信首先否定了使用BackgroundWorker的任何优势。您可以手动启动Thread,这不会有任何区别。顺便说一句,BackgroundWorker是technologically obsolete 恕我直言。 Async/await 让它变得无关紧要。 -
谢谢,很高兴知道!是的,退出
BackgroundWorker是长期目标之一——这是继承的,它是一个缓慢的新基地:)
标签: c# wpf multithreading