【问题标题】:Multiple timers accessing dictionary object in a singleton object多个计时器访问单例对象中的字典对象
【发布时间】:2012-02-22 10:22:19
【问题描述】:

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

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 进行任何锁定吗?

【问题讨论】:

    标签: c# multithreading timer


    【解决方案1】:

    是的,你必须这样做。

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

    也就是说阅读是线程安全的,但编辑不是。它还说迭代 Dictionary 并不安全。

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

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

    【讨论】:

    • 太棒了。我喜欢并发字典。非常感谢。
    【解决方案2】:

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

    例如,如果您预先确定了 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&lt;TKey,TValue&gt;

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多