簡體   English   中英

自我更新集合並發問題

[英]Self-updating collection concurrency issues

我正在嘗試建立一個自我更新的收藏集。 集合中的每個項目都有一個位置(x,y)。 更改位置后,將觸發一個事件,並且集合將重新定位該項目。

內部使用的是“鋸齒狀字典”。 外部字典使用x坐標作為鍵,而嵌套字典使用y坐標作為鍵。 然后,嵌套字典將項目列表作為值。

該集合還維護着一個字典,用於存儲嵌套字典中存儲的項位置-項到存儲的位置查找。

我在確保收集線程安全方面遇到了一些麻煩,這是我真正需要的。

集合的源代碼:

    public class PositionCollection<TItem, TCoordinate> : ICollection<TItem>
    where TItem : IPositionable<TCoordinate>
    where TCoordinate : struct, IConvertible
{
    private readonly object itemsLock = new object();
    private readonly Dictionary<TCoordinate, Dictionary<TCoordinate, List<TItem>>> items;
    private readonly Dictionary<TItem, Vector<TCoordinate>> storedPositionLookup;

    public PositionCollection()
    {
        this.items = new Dictionary<TCoordinate, Dictionary<TCoordinate, List<TItem>>>();
        this.storedPositionLookup = new Dictionary<TItem, Vector<TCoordinate>>();
    }

    public void Add(TItem item)
    {
        if (item.Position == null)
        {
            throw new ArgumentException("Item must have a valid position.");
        }

        lock (this.itemsLock)
        {
            if (!this.items.ContainsKey(item.Position.X))
            {
                this.items.Add(item.Position.X, new Dictionary<TCoordinate, List<TItem>>());
            }

            Dictionary<TCoordinate, List<TItem>> xRow = this.items[item.Position.X];

            if (!xRow.ContainsKey(item.Position.Y))
            {
                xRow.Add(item.Position.Y, new List<TItem>());
            }

            xRow[item.Position.Y].Add(item);

            if (this.storedPositionLookup.ContainsKey(item))
            {
                this.storedPositionLookup[item] = new Vector<TCoordinate>(item.Position);
            }
            else
            {
                this.storedPositionLookup.Add(item, new Vector<TCoordinate>(item.Position)); // Store a copy of the original position
            }

            item.Position.PropertyChanged += (object sender, PropertyChangedEventArgs eventArgs) => this.UpdatePosition(item, eventArgs.PropertyName);
        }
    }

    private void UpdatePosition(TItem item, string propertyName)
    {
        lock (this.itemsLock)
        {
            Vector<TCoordinate> storedPosition = this.storedPositionLookup[item];
            this.RemoveAt(storedPosition, item);
            this.storedPositionLookup.Remove(item);
        }

        this.Add(item);

    }
}

我編寫了一個簡單的單元測試來檢查並發問題:

        [TestMethod]
    public void TestThreadedPositionChange()
    {
        PositionCollection<Crate, int> collection = new PositionCollection<Crate, int>();
        Crate crate = new Crate(new Vector<int>(5, 5));
        collection.Add(crate);

        Parallel.For(0, 100, new Action<int>((i) => crate.Position.X += 1));

        Crate same = collection[105, 5].First();
        Assert.AreEqual(crate, same);
    }

每次我運行測試時,實際的存儲位置都會有所不同。 感謝您提供的任何反饋意見。

您可以為每個X坐標維護一個鎖列表,以提高並發性,否則,您不會看到很多性能提升。

或者,您可以切換到四叉樹或其他一些空間索引系統,以最大程度地減少項目移動時數據結構的干擾。 使用四叉樹,您可以通過在各個樹級別而不是整個數據結構上持有獨立的鎖來最大程度地減少並發問題。

您的數據結構使跟蹤移動的對象非常昂貴,因為它幾乎必須不斷更新自身。 使用“存儲桶式”方法(如果您已綁定到X / Y坐標),則可以將更新限制為僅當項目更改存儲桶時。

private void UpdatePosition(TItem item)
{
    // each bucket holds some MxN section of the X,Y grid
    var nextBucket = CalculateBucket(item.Position);
    if (nextBucket != item.Bucket)
    {
        lock (wholeCollectionLock)
        {
            this.buckets[nextBucket].Add(item);
            this.buckets[item.Bucket].Remove(item);
            item.Bucket = nextBucket;
        }
    }
}

public IEnumerable<TItem> ItemsAt(TPosition position)
{
    var bucket = CalculateBucket(position);
    lock (wholeCollectionLock)
    {
        // this will be efficient enough for cases where O(n) searches
        // have small enough n's
        // needs to be .ToArray for "snapshot"
        return this.buckets[bucket].Where(xx => xx.Position == position).ToArray();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM