2016-11-24 59 views
0

我的理解是,他們沒有。請確認。鎖確保列表元素的線程安全嗎?

假設我有一個包含列表的處理器對象<T>其中T是引用類型。

UI線程實例化處理器對象,並定期調用它來獲取列表<T>。

處理器對象在構建時還會啓動一個參考列表<T>的任務。

鎖定用以保留訪問列表<牛逼>在吸氣和任務,以確保他們的名單<牛逼>獨佔訪問。

public class Processor 
{ 
    private List<T> _list = new List<T>(); 
    private Object _listLock = new Object(); 

    public Processor() 
    { 
    // populate _list 

    Task t = new Task(Process); 

    t.Start(); 
    } 

    public List<T> GetList() 
    { 
    lock(_listLock) 
    { 
     return _list; 
    } 
    } 

    private void Process() 
    { 
    while(!doneProcessing) 
    { 
     lock(_listLock) 
     { 
     // access and modify _list items 
     } 

     Thread.Sleep(...); 
    } 
    } 
} 

但即使名單<牛逼>被鎖定在吸氣劑,它沒有問題返回列表參考,由處理器啓動任務仍在修改引用類型列表中的元素,當它抓住了鎖。

列表的元素仍然受到來自處理器的任務發生變化,並訪問它們在UI線程不會是線程安全的。

如果我是正確的,一個明顯的解決方案是,吸氣返回填充了列表元素的深層副本一個新的列表。

public List<T> GetList() 
{ 
    lock(_listLock) 
    { 
    return _list.Select(t => t.Clone()).ToList(); 
    } 
} 

你還能做什麼?

+0

不是答案,只是調用'ToList()'創建列表的副本。 – stuartd

+0

您只能公開您使用的'List '中的方法,而不是整個列表。這樣,您可以使用鎖定來訪問和修改項目。 –

+0

你是否清楚這樣一個事實:在修改列表中的元素時,像這樣的列表上鎖定時不會提供踩踏安全性? – Enigmativity

回答

0

你的GetList()不會做你認爲它的作用:

public List<T> GetList() 
    { 
    lock(_listLock) 
    { 
     return _list; 
    } 
    } 

由於的GetList()只返回一個參考_List,鎖()什麼都不做,除了防止2個線程得到一個參考同時列出,這不是問題。

問題是,您正在將列表對象的引用傳遞迴UI線程,並且引用指向的列表中的元素可能會隨時更改,而UI線程正在遍歷元素,這並不好。

您可以將鎖對象暴露給UI線程,但這意味着您的UI將需要在列表更新時阻塞,這通常是不合需要的(阻塞UI線程會降低用戶體驗)。

根據您的用戶界面對列表所做的操作,最好爲您的列表創建一個不可變的快照並將其返回給用戶界面。

如果您希望保持UI與基礎列表數據的更改一致,那麼您採用的方法實際上取決於UI技術。

2

鎖定,使用你的方式,不會確保線程安全。

如果您嘗試線程安全,請考慮使用.NET CLR中提供的thread safe collections之一。

你會注意到沒有線程安全的IList。 Here's why。線程安全列表的概念沒有多大意義。但是通過一些小的設計更改,您可以輕鬆使用類似ConcurrentDictionaryConcurrentBag的東西。