2010-02-28 1 views
8

Является ли C# структурой потокобезопасной?Являются ли C# структуры потоками безопасными?

Например, если есть:

struct Data 
{ 
    int _number; 
    public int Number { get { return _number; } set { _number = value; } } 

    public Data(int number) { _number = number; } 
} 

другого типа:

class DadData 
{ 
    public Data TheData { get; set; } 
} 

это свойство с именем TheData, поточно-?

+0

Это зависит. Что вы делаете со структурой? – SLaks

+2

Кроме того, вы не должны создавать изменчивую структуру. http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – SLaks

+0

В соответствии с этим http://stackoverflow.com/questions/2353014/are-c-structs-thread-safe/2353051#2353051 вам следует. –

ответ

8

Нет, структуры в .NET не являются потокобезопасными.

Однако семантика копирования по значению, структура которой имеет большое значение для этого преобразования.

Если вы передаете свои структуры вокруг и каким-то образом присвоите их переменным или параметрам pass-by-value (без ссылок или ссылок), тогда используется копия .

Конечно, это означает, что любые изменения, внесенные в копию, не отражаются в исходной структуре, но это то, о чем нужно знать при их прохождении.

Если вы обращаетесь к структуре напрямую в форме, не связанной с семантикой копирования по значению (например, для доступа к статическому полю, являющемуся типом структуры, и как Marc Gravel points out in his answer, существует много других способов) через несколько потоков, тогда вы должны учитывать безопасность потока экземпляра.

+0

И чтобы это ухудшилось, это также зависит от * большой * сделки, связан ли ваш код с полем *, * переменной * или * *. К счастью, в этом случае автоматически реализуемое свойство ('TheData') удаляет большинство из них. Поэтому я просто упомянул об этом для полноты ;-p –

+0

Спасибо, casperOne! Спасибо, Марк! Как выразился casperOne, я подумал: «семантика копирования по значению, структура которой имеет большое значение для этой беседы»; но не видел никаких указаний на рефренсы. Этот код не мой фактический код, а представление. Я пишу несколько многопоточных приложений; и некоторые общие инструменты параллельного программирования - C#; потому что некоторые шаблоны часто возвращаются;) (Конечно, я буду искать критику здесь, когда я выйду с чем-то достойным). –

-2

Нет. Почему это было бы поточно-безопасным? Это всего лишь данные. Он не становится потокобезопасным по магии.

1

A struct не более потокобезопасен, чем обычное поле или переменная. Если у вас есть хотя бы один поток, изменяющий его, и, по крайней мере, еще один поток, касающийся его каким-либо образом в одно и то же время, вы можете столкнуться с неожиданным/неопределенным поведением.

Также изменчивые структуры являются запахами кода. Есть ли какая-то конкретная причина, по которой вам нужно это быть struct вместо class? Вам нужна семантика значений для этих данных?

9

Хорошо - наилучшая практика заключается в том, что структуры должны всегда (за исключением нескольких конкретных сценариев и даже тогда в опасности) быть неизменными. И неизменяемые данные всегда надежны в потоке. Поэтому, если вы следовали лучшей практике и сделали это:

struct Data 
{ 
    readonly int _number; 
    public int Number { get { return _number; } } 

    public Data(int number) { _number = number; } 
} 

then yes; это поточно-безопасный. Во всех остальных случаях ответ «возможно, нет».

Следует также отметить, что правила атомарность применяются, так что даже один чтение или обновление DadData.TheData не может считаться поточно-, даже с неизменяемой структурой. Вы могли (особенно для негабаритных структур) иметь один поток, читающий структуру, а другой поток перезаписывает его; без синхронизации произойдут плохие вещи (в конце концов).

0

Прямые чтения и записи различных потоков изменяемой структуры не будут мешать друг другу. Доступ разных потоков к одному и тому же элементу через Interlocked методы будет вести себя в соответствии с семантикой этих методов. Эти факты могут позволить изменчивым структурам разрешать потокобезопасное поведение.

Устойчивые места хранения, содержащие структуры, которые не имеют никаких мутаций, кроме прямой замены, не предусматривают никакой безопасности потоков, за исключением того, что в тех случаях, когда структура содержит либо одно 32-битное целое число, либо ссылку на один объект, такое однопользовательское хранилище структурных данных одновременно с тем, как оно написано, гарантируется, что оно будет полностью считывать старые данные или совершенно новые данные. Обратите внимание, что невозможно использовать любой из методов Interlocked с неизменяемыми структурами - четными структурами, которые содержат только одну целую или объектную ссылку.

0

Нет, они не являются. Я создал очень простое приложение, чтобы узнать, получают ли 10/10 потоки производителя/потребителя одну и ту же структурную переменную. И в итоге вы увидите Debugger.Break(); будут удалены. Баланс банка не должен превышать 0.

namespace StructThreadSafe 
{ 
    class Program 
    { 
     struct BankBalance 
     { 
      public decimal Balance { get; set; } 
     } 

     static void Main(string[] args) 
     { 
      BankBalance bankBalance = new BankBalance(); 
      bankBalance.Balance = 100; 
      List<Task> allTasks = new List<Task>(); 
      for (int q = 0; q < 10; q++) 
      { 
       Task producer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         bankBalance.Balance += 5; 
         Console.WriteLine("++Current Balance: " + bankBalance.Balance); 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(producer); 
      } 
      for (int w = 0; w < 10; w++) 
      { 
       Task consumer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         if (bankBalance.Balance > 15) 
         { 
          bankBalance.Balance -= 15; 
          Console.WriteLine("--Current Balance: " + bankBalance.Balance); 
         } 
         else 
         { 
          Console.WriteLine("**Current Balance below minimum: " + bankBalance.Balance); 
         } 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(consumer); 
      } 
      allTasks.ForEach(p => p.Start()); 
      Task.WaitAll(allTasks.ToArray()); 

     } 
    } 
}