執行緒安全的GenericDictionary

餘二五發表於2017-11-15
 System.Collections.Generic.Dictionary<,>
只要不修改該集合,Dictionary 就可以同時支援多個閱讀器。即便如此,從頭到尾對一個集合進行列舉本質上並不是一個執行緒安全的過程。當出現列舉與寫訪問互相爭用這種極少發生的情況時,必須在整個列舉過程中鎖定集合。若要允許多個執行緒訪問集合以進行讀寫操作,則必須實現自己的同步。今天解決了使用Dictionary泛型類的時候出現一個錯誤 “System.InvalidOpervationException “集合已經修改,可能無法執行列舉操作””。原來的程式碼如下:
        private void CheckingTimeout() 

        {

            List
<string> list = new List<string>();

            
lock (sessions)

            {

                
foreach (string sessionKey in sessions.Keys)

                {

                    
if (sessions[sessionKey].IsTimeouted) 

                    {

                        logger.Info(
會話 [ + sessionKey + ] 超時);

                        UnLoadSession(sessionKey);

                        list.Add(sessionKey);

                    }

                }

                
foreach (string key in list)

                {

                    sessions.Remove(key);              

                }

            }

        } 


      
public void UnloadSession(string sessionID)

        {

            
lock (sessions)

            {

                
if (sessions.ContainsKey(sessionID))

                {

                    db.Delete(GetSessionFromDatabase(sessionID));

                    dispatcher.UnregisterAllOutEventSubscriber(sessionID);

                    sessions.Remove(sessionID);

                }

            }

        }


錯誤出現的原因是程式碼中在列舉的過程中修改了集合,而造成了這個錯誤,修改後的程式碼如下:
 


       private void CheckingTimeout()

        {

            List
<string> list = new List<string>();

            
lock (sessions)

            {

                
foreach (string sessionKey in sessions.Keys)

                {

                    
if (sessions[sessionKey].IsTimeouted)

                    {

                        logger.Info(
會話 [ + sessionKey + ] 超時);

                        UnregisterSession(sessionKey);

                        list.Add(sessionKey);

                    }

                }

                
foreach (string key in list)

                {

                    sessions.Remove(key);              

                }

            }

        }


        
private void UnregisterSession(string sessionID)

        {

            
lock (sessions)

            {

                
if (sessions.ContainsKey(sessionID))

                {

                    db.Delete(GetSessionFromDatabase(sessionID));

                    dispatcher.UnregisterAllOutEventSubscriber(sessionID);

                }

            }

        }


 
本文轉自 張善友 51CTO部落格,原文連結:http://blog.51cto.com/shanyou/75069,如需轉載請自行聯絡原作者


相關文章