2017-01-17 3 views
1

Мне поручено написать систему для обработки файлов результатов, созданных другим процессом (с которыми я не контролирую), и пытаюсь изменить мой код, чтобы использовать Parallel.Foreach. Код работает отлично, когда вы вызываете foreach, но у меня есть некоторые проблемы с безопасностью потоков при использовании параллельной версии. Базовый вопрос, на который мне нужно ответить, - «Я так делаю, чтобы гарантировать безопасность потока?» или это приведет к тому, что все пойдет ко мне.Есть частные свойства класса, называемого в Parallel.Foreach body Thread Safe?

Я попытался удостовериться, что все вызовы являются экземплярами и удалили все статические все, кроме начального статического void Main. По моему мнению, это будет делать многое для обеспечения безопасности потоков.

У меня в основном следующие, отредактированный для краткости

static void Main(string[] args) 
{ 
    MyProcess process = new MyProcess(); 
    process.DoThings(); 
} 

А потом в самом процессе, чтобы сделать вещи у меня есть

public class MyProcess 
{ 
    public void DoThings() 
    { 
     //Get some list of things 
     List<Thing> things = getThings(); 
     Parallel.Foreach(things, item => { 

      //based on some criteria, take actions from MyActionClass 
      MyActionClass myAct = new MyActionClass(item); 
      string tempstring = myAct.DoOneThing(); 

      if(somecondition) 
      { 
       MyAct.DoOtherThing(); 
      } 
      ...other similar calls to myAct below here 
     };    
    } 
} 

и снова в MyActionClass у меня есть что-то вроде следующего:

public class MyActionClass 
{ 
    private Thing _thing; 

    public MyActionClass(Thing item) 
    { 
     _thing = item; 
    } 

    public string DoOneThing() 
    { 
     return _thing.GetSubThings().FirstOrDefault(); 
    } 

    public void DoOtherThing() 
    { 
     _thing.property1 = "Somenewvalue"; 
    } 

} 

Если я могу объяснить это лучше, я попробую, но я думаю, что это основы моих потребностей

EDIT: Что-то еще я только что заметил. Если я изменяю значение свойства item Я работаю в то время как внутри Parallel.Foreach (в этом случае строковое значение, которое записывается в базу данных внутри цикла), будет иметь какое-либо влияние на остальную часть итерации цикла или только тот, на котором я нахожусь? Было бы лучше создать новый экземпляр Thing внутри цикла для хранения элемента, с которым я работаю в этом случае?

ответ

2

Там нет shared mutable state между действиями в Parallel.ForEach, что я можно увидеть, поэтому она должна быть поточно-, потому что в большинстве один поток может коснуться одного объекта одновременно.


Но, как уже упоминалось, нет ничего общего, что можно увидеть. Это не означает что в фактическом коде вы используете все так же хорошо, как кажется здесь.

Или что ничто не будет изменено вами или вашим коллегой, которое сделает какое-то состояние как общим, так и изменчивым (например, в Thing), и теперь вам становится трудно воспроизвести сбои в лучшем случае или просто неправильное поведение в что может быть оставлено незамеченным в течение длительного времени.

Итак, возможно, вам стоит попытаться пойти полностью immutable near threading код?

Возможно.

Immutability is good, но это не серебряная пуля, и она не всегда проста в использовании и реализации, или что каждая задача может быть разумно выражена через неизменные объекты. И даже это случайное «сделать совместное и изменчивое» изменение может случиться и с ним, хотя и гораздо менее вероятно.

Его следует по крайней мере рассматривать как возможный вариант/альтернативу.


О EDIT

Если изменить значение свойства этого пункта я работаю в то время как внутри Parallel.ForEach (в данном случае, строка значения, что получает , записанный в базу данных внутри цикла), будет ли какое-либо влияние на остальными итерациями цикла или только тем, на котором я нахожусь?

  1. Если вы изменить свойство и этот объект не используется нигде, и это не зависит от некоторого глобального изменяемого состояния (например, своего рода public static Int32 ChangesCount, что увеличивает с каждым изменением состояния) , тогда вы должны быть в безопасности.
  2. строковое значение, которое записывается в базу данных внутри цикла - в зависимости от используемой технологии доступа к данным и ее использования у вас могут быть проблемы, поскольку большинство из них не предназначены для многопоточной среды, например EF DbContext , например. И, очевидно, не забывайте, что иметь дело с concurrent access в базе данных не всегда легко, хотя это немного от нашей оригинальной темы.
  3. Было бы лучше создать новый экземпляр Thing внутри цикла для хранения элемента, с которым я работаю в этом случае - если нет риска внешних одновременных изменений, это просто лишняя работа. И если есть шанс на другие потоки (не Parallel.For), внося изменения в те объекты, которые сохраняются, тогда у вас уже есть больше проблем, чем Parallel.For.

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

И если они хотят быть сохранены с помощью внешнего кода, такие объекты, вероятно, следует предоставить:

  • Либо SyncRoot property для синхронизации свойств коды чтения.
  • Или какое-то текущее состояние моментального снимка DTO, который создается внутренне каким-то поточно-безопасным способом, например ThingSnapshot Thing.GetCurrentData() { lock() {} }.
  • Или что-то более экзотическое.
+0

Я попытался пройти весь код и убедиться, что нет вызовов статическим методам. Я пошел дальше и заменил все статические методы на экземпляры и старался не вызывать ничего из созданного вне 'Parallel.Foreach' изнутри цикла (за исключением фактического ** элемента **, с которым я выполняю итерацию) , Я просто очень новичок в потоковом в целом, и у меня есть немного времени, обертывающего мою голову вокруг всего этого. Как показано выше, в живом коде я ввел 'Parallel.Foreach' почти сразу после ввода метода и делал все вызовы из него в экземпляры. – Rocky

+0

Редактировать: добавлено в базовый вопрос – Rocky

+0

Я думаю, что неизменяемость может работать здесь, так как обычно мне не нужно менять какой-либо элемент в списке, кроме одной строки, и, как сказано, я думаю, что могу просто создать новый экземпляр класса и передать ** элемент ** в это для этого.В конце концов, я просто читаю строки в файле и предпринимаю некоторые действия на их основе. Я займусь этим. – Rocky