C # блокировка и общий вопрос проектирования потоков - PullRequest
2 голосов
/ 19 августа 2009

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

В настоящее время программа загружает список данных и сегментирует эти данные на разделы (один блок для каждого потока). Затем программа инициализирует новый поток, используя ThreadPool, и передает ему ОДИН сегмент сегментированных данных для работы.

Все работает хорошо ... кроме:

Некоторые потоки не работают ... из-за проблем с сетью или неисправимых исключений. Это ожидаемое поведение, а не ошибка.

Теперь мне нужен способ (в случае сбоя потока) восстановить сегмент данных этого потока и передать его другому рабочему потоку, чтобы он не потерял целостность. Я уверен, что есть способы сделать это, например, обмен данными между потоками и т. Д., Но я думаю, что есть лучший подход.

Вместо того, чтобы предварительно сегментировать данные и передавать их каждому потоку, я мог бы разделить ОДНУ статическую коллекцию этих данных между всеми потоками. Это более элегантно, но вводит новые проблемы с синхронизацией, о которых старому подходу не нужно было беспокоиться.

A.) Что вы думаете об этом подходе по сравнению со старым?
B.) Если этот подход хорош, как мне заблокировать доступ к общей статической коллекции.

Когда поток запускается, я могу заблокировать коллекцию и выдвинуть сегмент данных только для этого потока. Статическая коллекция теперь будет СНИЖЕНА на количество, потраченное на этот поток. После сбоя потока я мог перераспределить этот сегмент данных в общую коллекцию, снова заблокировав его и отправив данные обратно в коллекцию, чтобы другие потоки попытались обработать их.

Например: (непроверенный псевдокод)

void Process(object threadInfo)
{
  lock(StaticCollection)
  {
    var segment = StaticCollection.Take(100);
    StaticCollection.Remove(StaticCollection.Where(item => segment.Contains(item)))
  }

  foreach(var seg in segment)
  {
    // do something
  }

  // reallocate the thread's data on failure
  if(unrecoverableErrorOccurred)
  {
    lock(StaticCollection)
    {
      StaticCollection.Add(segment);
    }
  }
}

Я на правильном пути с этим? Мне кажется, что один поток может удалять элементы одновременно, а другой поток перераспределяет элементы ... или делает блокировку для коллекции STATIC, что означает, что никакой другой поток не может получить доступ к этой коллекции вообще. Итак, Поток А.) получил блокировку в ПЕРВОЙ части метода, блокирует ли это все остальные потоки от выполнения части LAST метода до тех пор, пока Поток A не будет завершен?

Ответы [ 2 ]

4 голосов
/ 19 августа 2009

Давайте выделим несколько вещей здесь ...

Во-первых, вы не на самом деле блокируете коллекцию. Вы блокируете монитор, связанный с объектом. Лично я считаю, что было ошибкой, что .NET последовал за Java, предоставив каждому объекту связанный монитор для блокировки, но давайте оставим это в стороне. Лично я предпочитаю иметь объекты и связанные переменные чисто для блокировки - так что в моем коде вы можете увидеть:

private readonly object padlock = new object();

Это гарантирует, что no другой код попытается получить эту блокировку, потому что они не будут знать об объекте.

Во-вторых, замки рекомендательные. Это часть бизнеса «ты не блокируешь коллекцию». Если сама коллекция синхронизируется с одной и той же блокировкой - и неуниверсальные коллекции имеют метод Synchronized для этой цели - но в основном, если что-то явно не снимает блокировку, вы не получите синхронизацию.

В-третьих, да, два заблокированных блока, показанные в вашем коде , используют одну и ту же блокировку (при условии, что значение StaticCollection, конечно, не изменится). Если один поток занят вызовом Remove, это остановит любой другой поток от одновременного вызова Add, поскольку каждый из них должен иметь блокировку. Это, вероятно, то, что вы хотите.

Лично я бы не стал действительно статической коллекцией (точнее, я бы не использовал переменную StaticCollection). Я бы дал каждой задаче ссылку на одну и ту же коллекцию (и ссылку на связанную блокировку; на самом деле, я бы, вероятно, инкапсулировал коллекцию, синхронизацию и «принеси мне кучу работы» и «вот куча работы для иметь задние «биты в отдельном классе). Это упростит тестирование и в целом логичнее. Это также означает, что у вас может быть два отдельных «набора» потоков, работающих над разными коллекциями одновременно ... что может быть полезно, если вы сделаете вышеуказанную инкапсуляцию универсальной, чтобы они могли выполнять радикально разные задачи ...

0 голосов
/ 19 августа 2009

Вы можете рассмотреть возможность использования очереди для хранения необработанных фрагментов, и, как говорит Джон Скит, заблокируйте нейтральный объект и удерживайте блокировку только достаточно долго, чтобы получить доступ к очереди. Я использовал этот подход со многими потоками, и он работал хорошо для меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...