『System.ArgumentException 目的陣列不夠長。請檢查 destIndex 與長度,以及陣列的下限 』List執行緒問題解決方法

2019/10/15 C#

錯誤解說

今天公司的程式出現了一個Bug,錯誤如下:

System.ArgumentException: 目的陣列不夠長。請檢查 destIndex 與長度,以及陣列的下限。

後來發現是下面這段Code的問題:

dataGridView.DataSource = new List<Log>(LogInfos);

因為LogInfos這個List會被其他的執行緒改動,所以在把LogInfos丟進new List<>()裡面觸發Array.Copy到新的List的同時被其他執行緒更動到了,所以才發生錯誤!

解決方法

後來查資料後找到一個不用在程式碼加Lock的方法。

LogInfos再用一個自定義的物件包起來,如下:

public class Container<T>
{
    private ImmutableList<T> _items = ImmutableList<T>.Empty;
    private IReadOnlyList<T> Items { get { return _items; } }
    public void Add(T item) { _items = _items.Add(item); }
    public List<T> ToList() 
    {
        return Items.ToList();
    }
}

裡面的資料結構使用ImmutableList,然後要存取資料的時候用IReadOnlyList,把AddGet分開,這樣執行緒就不會互相衝突了!

但是這麼做會有幾個缺點:

  1. 因為ImmutableList內部是用BinaryTree去實現的,所以Add的時間複雜度不是O(1)而是O(log N)!所以如果會頻繁的Add的話就不建議用。
  2. 有可能會發生Stale Read,意思就是存取資料的時候,資料並沒有反應到最近一次的更新。這個狀況會發生在ToList的同時有人Add,這樣雖然不會有Exception,但是讀到的資料不會包括同時新增的那一筆。 所以這個方法要能容許這個狀況的發生。

Search

    Table of Contents