多个计时器访问单个对象中的字典对象

时间:2012-02-22 01:37:38

标签: c# multithreading timer

我有一个单例对象,并在其中定义了一个字典。

public class MyClass 
{
    public static readonly MyClass Instance = new MyClass();

    private MyClass
    {}

    public Dictionary<int, int> MyDictionary = new Dictionary<int, int>();
}

现在,我有两个System.Timers.Timer对象更新MyDictionary。

System.Timers.Timer timer1 = new System.Timers.Timer(5);
timer1.AutoReset = false;
timer1.Elapsed += new System.Timers.ElapsedEventHandler(MyTimer1Handler);
timer1.Enabled = true;
timer1.Start();

System.Timers.Timer timer2 = new System.Timers.Timer(5);
timer2.AutoReset = false;
timer2.Elapsed += new System.Timers.ElapsedEventHandler(MyTimer2Handler);
timer2.Enabled = true;
timer2.Start();

private void MyTimer1Handler(object sender, ElapsedEventArgs e)
{
     MyClass.Instance.MyDictonary[1] = 100;
}

private void MyTimer1Handler(object sender, ElapsedEventArgs e)
{
     MyClass.Instance.MyDictonary[2] = 100;
}

现在我的问题是,考虑到定时器的已用事件处理程序在MyDictionary的索引1和索引2上唯一操作,我是否需要对MyDictionary进行任何锁定?

2 个答案:

答案 0 :(得分:5)

是的,你必须。

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

这表示读取是线程安全的,但编辑不是。它还说迭代Dictionary是不安全的。

如果您能够使用.NET 4 ,则可以使用线程安全的ConcurrentDictionary

http://msdn.microsoft.com/en-us/library/dd287191.aspx

答案 1 :(得分:1)

对于您发布的这个具体示例,是的,您必须这样做,但严格来说,根据您的使用模式,并不总是必要的。

例如,如果您预先确定了2个密钥,那么如果一个线程操作不影响其他线程操作的状态,则不会修改字典的共享状态。例如,如果您知道您没有添加/删除密钥,则每个线程将访问特定密钥。

让我们考虑以下简化示例,其中我们只是并行地递增2个给定键的先前值:

class Program
{

    static Dictionary<string, int> _dictionary = new Dictionary<string, int>();

    static void Main(string[] args)
    {
        _dictionary["key1"] = 0;
        _dictionary["key2"] = 0;

        Action<string> updateEntry = (key) =>
            {
                for (int i = 0; i < 10000000; i++)
                {
                    _dictionary[key] = _dictionary[key] + 1;
                }
            };

        var task1 = Task.Factory.StartNew(() =>
            {
                updateEntry("key1");
            });

        var task2 = Task.Factory.StartNew(() =>
        {
            updateEntry("key2");
        });

        Task.WaitAll(task1, task2);

        Console.WriteLine("Key1 = {0}", _dictionary["key1"]);
        Console.WriteLine("Key2 = {0}", _dictionary["key2"]);

        Console.ReadKey();
    }
}

您认为在同一个字典中同时在2个单独个线程中迭代超过1000万次后,字典中每个键的值是多少?在循环中?

嗯,你得到了

Key1 = 10000000
Key2 = 10000000

上述示例中无需额外同步,只需将值分配给字典中的现有键即可。

当然,如果您想添加或删除密钥然后,您需要考虑同步或使用数据结构,例如ConcurrentDictionary<TKey,TValue>

在您的情况下,您实际上是在字典中添加值,因此您必须使用某种形式的同步。