2013-04-08 2 views
9

Я пытаюсь увеличить элемент в списке на C#, но мне нужно, чтобы он был потокобезопасным, поэтому счет не влияет.Thread safe Increment in C#

Я знаю, что вы можете сделать это для целых чисел:

Interlocked.Increment(ref sdmpobjectlist1Count);

, но это не работает в списке я следующий до сих пор:

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

Я знаю, что это работает, но Я не уверен, есть ли другой способ сделать это?

+14

Зачем вам нужен другой способ, если этот способ работает? –

+0

Вы ищете неэксклюзивный механизм блокировки? –

+0

http://msdn.microsoft.com/en-us/library/system.threading.monitor.aspx – CSharpie

ответ

1

Как сказал Дэвид Хеффернан, 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)); 
     } 
    } 
} 
+0

Функция обновления не является атомарной при вызове AddOrUpdate. https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx – Ryan

+0

Этот комментарий просто означает, что «код может запускаться более одного раза, прежде чем он удастся». В основном он выходит за пределы замка. Таким образом, он получает существующее значение, добавляет 1 к нему, вынимает блокировку и затем пытается записать ее обратно. Если существующее значение изменилось в то же время, оно повторяет процесс до тех пор, пока он не удастся. – mjwills

-1

проверить переменную, которую вы заблокировали на «padLock», как правило, вы можете определить ее как private static Object padLock = new Object(). если вы не определяете его как статичный, каждый объект имеет свою собственную копию, поэтому он не будет работать.

+4

Это зависит от данных. Если данные не являются статическими, нет необходимости, чтобы объект блокировки был статическим; вы не нуждаетесь в синхронизации с потоками, которые не пытаются получить доступ к тем же данным. – Servy

1

Если вы используете List<int[]>, а не List<int>, и есть каждый элемент в списке будет массив одного пункта, вы будете в состоянии сделать Increment(ref List[whatever][0]) и он будет атомным. Можно повысить эффективность хранения немного, если один определенный

class ExposedFieldHolder<T> {public T Value;} 

, а затем использовали List<ExposedFieldHolder<int>> и использовали выражение Increment(ref List[whatever].Value) выполнить приращение. Вещи могут быть более эффективными, если встроенные типы предоставляют средство для выставления элемента как ref или позволяют производным классам иметь достаточный доступ к их внутренним элементам, чтобы обеспечить такую ​​возможность сами. Тем не менее, они не должны определять собственные типы коллекций с нуля или инкапсулировать каждый элемент в свой собственный объект класса [с использованием массива или класса оболочки].

+0

Если у вас есть какая-то особая причина для написания собственной потоковой безопасной коллекции, в большинстве случаев вам будет намного лучше, если вы предоставите TPL. –