实现线程安全的Dictionary<TKey, TValue>

1.  不安全的泛型集合Dictionary<TKey, TValue>

泛型集合Dictionary<TKey, TValue>不是线程安全的。在多线程环境下读写Dictionary<TKey, TValue>,程序经常抛出异常。下面程序中,启动两个线程,遍历集合(读操作);启动一个线程,每秒向集合中添加一个项。

using System;
using System.Collections.Generic;
using System.Threading;

namespace DictionaryTest
{
    class User
    {
        public string Name { get; set; }
        public string Password { get; set; }
        public DateTime OnlineTime { get; set; }
        public bool IsAdministrator { get; set; }
    }
    
    class Program
    {
        static Dictionary<string, User> userCache 
            = new Dictionary<string, User>();
        
        static void Main(string[] args)
        {
            // 启动两个读线程
            new Thread(ReadCache) { IsBackground = true }.Start();
            new Thread(ReadCache) { IsBackground = true }.Start();
            // 启动一个写线程
            new Thread(WriteCache) { IsBackground = true }.Start();
            Console.ReadLine();
        }

        static void ReadCache()
        {
            while (true)
            {
                foreach (var item in userCache)
                {
                    Thread.Sleep(5);
                }
            }
        }

        static void WriteCache()
        {
            int id = 0;
            while (true)
            {
                User user = new User()
                {
                    Name = "user" + id.ToString(),
                    Password = id.ToString("d7")
                };
userCache.Add(user.Name, user); Thread.Sleep(1000); } } } }

程序运行后,很快抛出异常

2. 实现线程安全的泛型集合SafeDictionary<TKey, TValue>

利用System.Thread. ReaderWriterLockSlim锁实现线程安全的泛型集合SafeDictionary<TKey, TValue>。SafeDictionary<TKey, TValue>支持多个线程并发读取;

    public class SafeDictionary<TKey, TVlue> : IEnumerable<KeyValuePair<TKey, TVlue>>
    {
        private readonly Dictionary<TKey, TVlue> storage;
        private ReaderWriterLockSlim rwLock;

        public SafeDictionary()
        {
            rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
            storage = new Dictionary<TKey, TVlue>();
        }
        public TVlue this[TKey key]
        {
            get
            {
                TVlue value = default(TVlue);
                try
                {
                    rwLock.EnterReadLock();
                    storage.TryGetValue(key, out value);
                }
                finally
                {
                    rwLock.ExitReadLock();
                }
                return value;
            }
        }
        public void Add(TKey key, TVlue value)
        {
            try
            {
                rwLock.EnterUpgradeableReadLock();
                if (storage.ContainsKey(key)) return;
                try
                {
                    rwLock.EnterWriteLock();
                    storage.Add(key, value);
                }
                finally
                {
                    rwLock.ExitWriteLock();
                }
            }
            finally
            {
                rwLock.ExitUpgradeableReadLock();
            }
        }
        public bool Remove(TKey key)
        {
            try
            {
                rwLock.EnterWriteLock();
                return storage.Remove(key);
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }
        public List<KeyValuePair<TKey, TVlue>> ToList()
        {
            try
            {
                rwLock.EnterReadLock();
                return storage.ToList();
            }
            finally
            {
                rwLock.ExitReadLock();
            }
        }
        public List<TKey> GetKeyList()
        {
            try
            {
                rwLock.EnterReadLock();
                return storage.Keys.ToList();
            }
            finally
            {
                rwLock.ExitReadLock();
            }
        }
        public List<TVlue> GetValueList()
        {
            try
            {
                rwLock.EnterReadLock();
                return storage.Values.ToList();
            }
            finally
            {
                rwLock.ExitReadLock();
            }
        }
        public int Count
        {
            get
            {
                try
                {
                    rwLock.EnterReadLock();
                    return storage.Count;
                }
                finally
                {
                    rwLock.ExitReadLock();
                }
            }
        }
        public void Clear()
        {
            try
            {
                rwLock.EnterWriteLock();
                storage.Clear();
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }
        public IEnumerator<KeyValuePair<TKey, TVlue>> GetEnumerator()
        {
            try
            {
                rwLock.EnterReadLock();
                foreach (var item in storage)
                {
                    yield return item;
                }
            }
            finally
            {
                rwLock.ExitReadLock();
            }
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

ReaderWriterLockSlim提供两种锁定方式:“写锁定(wirte lock)”和“读锁定(read lock)”。任何时刻,ReaderWriterLockSlim只允许1个线程获取“写锁定”。获取了“写锁定”的线程将阻止其他已经获取“读锁定”的线程对资源的读取操作;如果当前没有获取“写锁定”的线程,那么,多个获取“读锁定”的线程可以同时对资源进行读取操作。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace SafeDictionaryTest
{
    class Program
    {
        static SafeDictionary<string, User> userCache;
        static volatile int id = 0;
        
        static void Main(string[] args)
        {
            userCache = new SafeDictionary<string, User>();
            // 两个读线程
            new Thread(ReadCache) { IsBackground = true }.Start();
            new Thread(ReadCache) { IsBackground = true }.Start();
            // 两个写线程
            new Thread(WriteCache) { IsBackground = true }.Start();
            new Thread(WriteCache) { IsBackground = true }.Start();
            // 两个删除线程
            new Thread(DeletUser) { IsBackground = true }.Start();
            new Thread(DeletUser) { IsBackground = true }.Start();

            Console.ReadLine();
        }

        static void ReadCache()
        {
            while (true)
            {
                foreach (var v in userCache)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("{0} {1}", v.Key, v.Value.Password);
                }
                Console.WriteLine("-------------------------------");
                Thread.Sleep(1000);
            }
        }

        static void WriteCache()
        {
            while (true)
            {
                id++;
                string name = "user" + id.ToString();
                string password = id.ToString("d7");
                User user = new User() { Name = name, Password = password };
                userCache.Add(name, user);
                Thread.Sleep(1000);
            }
        }

        static void DeletUser()
        {
            Random rnd = new Random();
            while (true)
            {
                string key = "user" + rnd.Next(1, id).ToString();
                if (userCache.Remove(key))
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("{0} Deleted", key);
                }
                Thread.Sleep(50);
            }
        }
    }
}

 

posted @ 2014-08-04 17:58  protorock  阅读(294)  评论(0)    收藏  举报