ObservableCollection unhandled exception Destination array is not long enought to copy all the items in the collection

huangapple go评论57阅读模式
英文:

ObservableCollection unhandled exception Destination array is not long enought to copy all the items in the collection

问题

我的应用程序是PLC设备的多线程实时处理程序。它有时会出现未处理的异常:

--- 未处理的异常:
类型:ArgumentException
消息:目标数组的长度不足以复制集合中的所有项。请检查数组索引和长度。
堆栈: 在 System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) 处引发异常
在 System.Collections.ObjectModel.Collection`1.System.Collections.ICollection.CopyTo(Array array, Int32 index) 处
在 System.Collections.ArrayList.InsertRange(Int32 index, ICollection c) 处
在 System.Collections.ArrayList.AddRange(ICollection c) 处
在 System.Collections.ArrayList..ctor(ICollection c) 处
在 System.Windows.Data.ListCollectionView.b__1_0() 处
在 MS.Internal.Data.SynchronizationInfo.AccessCollection(IEnumerable collection, Action accessMethod, Boolean writeAccess) 处
在 System.Windows.Data.BindingOperations.AccessCollection(IEnumerable collection, Action accessMethod, Boolean writeAccess) 处
在 System.Windows.Data.ListCollectionView.RefreshOverride() 处
在 System.Windows.Data.CollectionView.RefreshInternal() 处
在 System.Windows.Data.CollectionView.RefreshOrDefer() 处
在 System.Windows.Data.ListCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args) 处
在 System.Windows.Data.CollectionView.ProcessChangeLog(ArrayList changeLog, Boolean processAll) 处
在 System.Windows.Data.CollectionView.ProcessInvoke(Object arg) 处
在 MS.Internal.Data.DataBindOperation.Invoke() 处
在 MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests() 处
在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) 处
在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) 处
在 System.Windows.Threading.DispatcherOperation.InvokeImpl() 处
在 System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) 处
在 MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj) 处
在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 处
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 处
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 处
在 MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state) 处
在 System.Windows.Threading.DispatcherOperation.Invoke() 处
在 System.Windows.Threading.Dispatcher.ProcessQueue() 处
在 System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) 处
在 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) 处
在 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) 处
在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) 处
在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) 处
在 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) 处
在 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) 处
在 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) 处
在 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) 处
在 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) 处
在 System.Windows.Application.RunDispatcher(Object ignore) 处
在 System.Windows.Application.RunInternal(Window window) 处
在 System.Windows.Application.Run(Window window) 处
在 System.Windows.Application.Run() 处
在 mes3cs.App.Main() 处

只有 mes3cs.App.Main() 在我的代码中。

我猜这与使用 ObservableCollection 有关。我是这样使用它的:

class DevMeasure {
    public readonly ObservableCollection<MsgMeasure> _measures = new ObservableCollection<MsgMeasure>();
    private readonly object _measuresLock = new object();
}

在初始化代码中:

BindingOperations.EnableCollectionSynchronization(_measures, _measuresLock);

然后将 _measures 列表显示在 DataGrid 中:

dgMeasures.ItemsSource = _devMeasure._measures;

为什么它会导致崩溃?我以为 ObservableCollection 是线程安全的,不是吗?

英文:

My app is mutli thread real time handler of plc devices. It crashes sometimes with unhandled exception:

--- Unhandled Exception:
Type:ArgumentException
Message:Destination array is not long enough to copy all the items in the collection. Check array index and length.
Stack:   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.ObjectModel.Collection`1.System.Collections.ICollection.CopyTo(Array array, Int32 index)
   at System.Collections.ArrayList.InsertRange(Int32 index, ICollection c)
   at System.Collections.ArrayList.AddRange(ICollection c)
   at System.Collections.ArrayList..ctor(ICollection c)
   at System.Windows.Data.ListCollectionView.&lt;RefreshOverride&gt;b__1_0()
   at MS.Internal.Data.SynchronizationInfo.AccessCollection(IEnumerable collection, Action accessMethod, Boolean writeAccess)
   at System.Windows.Data.BindingOperations.AccessCollection(IEnumerable collection, Action accessMethod, Boolean writeAccess)
   at System.Windows.Data.ListCollectionView.RefreshOverride()
   at System.Windows.Data.CollectionView.RefreshInternal()
   at System.Windows.Data.CollectionView.RefreshOrDefer()
   at System.Windows.Data.ListCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
   at System.Windows.Data.CollectionView.ProcessChangeLog(ArrayList changeLog, Boolean processAll)
   at System.Windows.Data.CollectionView.ProcessInvoke(Object arg)
   at MS.Internal.Data.DataBindOperation.Invoke()
   at MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests()
   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&amp; handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp; 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&amp; 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 mes3cs.App.Main()

Only mes3cs.App.Main() is in my code.

I guess this is related to using ObservableCollection. I use it this way:

class DevMeasure {
	public readonly ObservableCollection&lt;MsgMeasure&gt; _measures = new ObservableCollection&lt;MsgMeasure&gt;();
	private readonly object _measuresLock = new object();
}

And in the init code:

		BindingOperations.EnableCollectionSynchronization(_measures, _measuresLock);

And show this _measures list in a DataGrid:

   dgMeasures.ItemsSource = _devMeasure._measures;

Why it is causing crash? I thought ObservableCollection is all thread safe, isn't it?

答案1

得分: 4

我认为ObservableCollection是线程安全的,不是吗?

不是!除非另有文件记录,否则应假定所有类型都不是线程安全的。

必须确保对集合的所有访问都是同步的。仅仅调用EnableCollectionSynchronization是不够的,您还必须在对集合进行任何更改时锁定_measuresLock对象。EnableCollectionSynchronization调用只是告诉wpf在访问集合之前也需要获取锁。有关详细说明,请阅读文档

但是,我建议只需确保所有与UI相关的代码在UI线程上运行。创建一些明确定义的、线程安全的接口,用于在工作线程和UI线程之间传输数据,例如blocking collection。这可能有助于在后台线程和UI线程之间创建更清晰的分离。

请注意,多线程开发是困难的。它引入了一堆新的可能问题,而且大多数问题都很难调试。因此,为了避免问题首次出现,您确实需要知道自己在做什么。假设类是线程安全的会很可能导致错误逃逸到生产环境,从而可能造成真正的损害。

英文:

> I thought ObservableCollection is all thread safe, isn't it?

No! Unless otherwise documented, all types should be assumed to be non thread safe.

You have to ensure all access to the collection is synchronized. Simply calling EnableCollectionSynchronization is not enough, you must also lock the _measuresLock object whenever you are making any change to the collection. The EnableCollectionSynchronization call is simply there to tell wpf it also need to take a lock before accessing the collection. For detailed instructions, read the documentation.

However, I would consider just ensuring that all UI related code is running on the UI thread instead. Create some well defined, thread safe, interface for moving data between the worker threads and the UI thread, for example a blocking collection. That might help to create a clearer separation between background threads and the UI thread.

Note that multi threaded development is difficult. It introduces a bunch of new possible issues, and most of them are really difficult to debug. So you really need to know what you are doing to avoid problems in the first place. Making assumptions about classes being thread safe will very likely lead to bugs escaping to production where they could do real damage.

答案2

得分: 0

谢谢大家!我明白ObservableCollection现在不是线程安全的。最终,我编写了一个名为MyObservableCollection的类,以确保它是线程安全的。它已经连续运行了1周没有崩溃。

using System.Collections.ObjectModel;
using System.Windows.Data;

namespace mes3cs.common
{
    public class MyObservableCollection<T> : ObservableCollection<T>
    {
        private object _lock = new object();
        
        public MyObservableCollection()
        {
            BindingOperations.EnableCollectionSynchronization(this, _lock);
        }
        
        new public void Add(T item)
        {
            lock (_lock)
            {
                base.Add(item);
            }
        }
        
        new public void Clear()
        {
            lock (_lock)
            {
                base.Clear();
            }
        }
        
        new public void CopyTo(T[] array, int arrayIndex)
        {
            lock (_lock)
            {
                base.CopyTo(array, arrayIndex);
            }
        }
        
        new public void Insert(int index, T item)
        {
            lock(_lock)
            {
                base.Insert(index, item);
            }
        }
        
        new public void InsertItem(int index, T item)
        {
            lock (_lock)
            {
                base.InsertItem(index, item);
            }
        }
        
        new public void Remove(T item)
        {
            lock (_lock)
            {
                base.Remove(item);
            }
        }
        
        new public void RemoveAt(int index)
        {
            lock (_lock)
            {
                base.RemoveAt(index);
            }
        }
        
        new public void RemoveItem(int index)
        {
            lock (_lock)
            {
                base.RemoveItem(index);
            }
        }
        
        new public void SetItem(int index, T item)
        {
            lock(_lock)
            {
                base.SetItem(index, item);
            }
        }
    }
}
英文:

Thanks all! I understand ObservableCollection is not thread safe now. Finally I wrote a MyObservableCollection to make it thread safe. And it has been running continuously for 1 week without crash.

using System.Collections.ObjectModel;
using System.Windows.Data;
namespace mes3cs.common
{
public class MyObservableCollection&lt;T&gt; : ObservableCollection&lt;T&gt;
{
private object _lock = new object();
public MyObservableCollection()
{
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
new public void Add(T item)
{
lock (_lock)
{
base.Add(item);
}
}
new public void Clear()
{
lock (_lock)
{
base.Clear();
}
}
new public void CopyTo(T[] array, int arrayIndex)
{
lock (_lock)
{
base.CopyTo(array, arrayIndex);
}
}
new public void Insert(int index, T item)
{
lock(_lock)
{
base.Insert(index, item);
}
}
new public void InsertItem(int index, T item)
{
lock (_lock)
{
base.InsertItem(index, item);
}
}
new public void Remove(T item)
{
lock (_lock)
{
base.Remove(item);
}
}
new public void RemoveAt(int index)
{
lock (_lock)
{
base.RemoveAt(index);
}
}
new public void RemoveItem(int index)
{
lock (_lock)
{
base.RemoveItem(index);
}
}
new public void SetItem(int index, T item)
{
lock(_lock)
{
base.SetItem(index, item);
}
}
}
}

huangapple
  • 本文由 发表于 2023年6月5日 17:39:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76405156.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定