【问题标题】:Thread safe Increment in C#C#中的线程安全增量
【发布时间】:2013-03-31 01:46:41
【问题描述】:

我正在尝试在 C# 中增加列表中的元素,但我需要它是线程安全的,因此计数不会受到影响。

我知道你可以对整数这样做:

Interlocked.Increment(ref sdmpobjectlist1Count);

但这不适用于我目前拥有以下列表的列表:

lock (padlock)
{
     DifferenceList[diff[d].PropertyName] = DifferenceList[diff[d].PropertyName] + 1;
}

我知道这行得通,但我不确定是否有其他方法可以做到这一点?

【问题讨论】:

标签: c# multithreading thread-safety increment


【解决方案1】:

检查你锁定在“padLock”上的变量,通常可以定义为private static Object padLock = new Object()。如果不将其定义为静态,则每个对象都有自己的副本,因此将无法正常工作。

【讨论】:

  • 这取决于数据。如果数据不是静态的,那么锁对象就不需要是静态的;您将不必要地与未尝试访问相同数据的线程同步。
【解决方案2】:

正如 David Heffernan 所说,ConcurrentDictionary 应该提供更好的性能。但是,性能提升可能可以忽略不计,具体取决于多个线程尝试访问缓存的频率。

using System;
using System.Collections.Concurrent;
using System.Threading;

namespace ConcurrentCollections
{
    class Program
    {
        static void Main()
        {
            var cache = new ConcurrentDictionary<string, int>();

            for (int threadId = 0; threadId < 2; threadId++)
            {
                new Thread(
                    () =>
                    {
                        while (true)
                        {
                            var newValue = cache.AddOrUpdate("key", 0, (key, value) => value + 1);
                            Console.WriteLine("Thread {0} incremented value to {1}",
                                Thread.CurrentThread.ManagedThreadId, newValue);
                        }

                    }).Start();
            }

            Thread.Sleep(TimeSpan.FromMinutes(2));
        }
    }
}

【讨论】:

  • 更新函数在 AddOrUpdate 调用中不是原子的。 msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
  • 该注释仅表示“代码在成功之前可能会运行多次”。它基本上在锁外运行。因此,它获取现有值,将其加 1,取出锁,然后尝试将其写回。如果同时现有值发生了变化,则重复该过程直到成功。
【解决方案3】:

如果您使用List&lt;int[]&gt; 而不是List&lt;int&gt;,并且列表中的每个元素都是一个单项数组,那么您将能够使用Increment(ref List[whatever][0]) 并使其成为原子。如果定义一个,可以稍微提高存储效率

class ExposedFieldHolder<T> {public T Value;}

然后使用List&lt;ExposedFieldHolder&lt;int&gt;&gt; 并使用语句Increment(ref List[whatever].Value) 执行增量。如果内置类型提供了一种将项目公开为ref 的方法,或者允许派生类充分访问其内部以提供这种能力,事情可能会更有效。然而,它们没有,因此必须要么从头开始定义自己的集合类型,要么将每个项目封装在其自己的类对象中[使用数组或包装类]。

【讨论】:

  • 除非在大多数情况下您有一些非常具体的原因要编写自己的线程安全集合,否则使用 TPL 提供的集合会好得多。
猜你喜欢
  • 2022-08-17
  • 1970-01-01
  • 2010-12-14
  • 2017-04-14
  • 2014-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多