Многопоточность C # - Сообщать ожидающему процессу, что первый процесс завершился с использованием заблокированного кода?

2

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

Вопрос

Как вы сообщаете процессу ожидания (proc2), что процесс, использующий заблокированный блок кода (proc1), завершил использование кода?

Теги:
multithreading
locking
monitor

5 ответов

5
Лучший ответ

Вы говорите о реальных процессах операционной системы или просто о задачах в процессе?

Если это просто задачи внутри процесса, вы обычно просто позволяете блокировщикам выполнять эту работу за вас. Вторая задача пытается получить блокировку, которой владеет первая задача, и просто блокирует. Когда первая задача освобождает блокировку, вторая задача автоматически разблокируется.

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

Если вы хотите подождать, но у вас есть более сложные требования, чем просто блокировка, то, вероятно, требуется Monitor.Pulse/PulseAll/Wait. Для примера см. Вторую половину эту страницу.

Если вы действительно говорите о процессах, вам необходимо использовать общесистемную структуру, такую ​​как Mutex. Они являются "более тяжелыми", чем встроенные мониторы .NET, но допускают координацию между процессами.

1

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

Другими словами, в типичном сценарии вы не "рассказываете" другому потоку, а не останавливаете его.

(Существуют сценарии, в которых вы хотите указать поток, который обычно обрабатывается с помощью AutoResetEvent или ManualResetEvent)

1

Для этого вам нужно использовать различные объекты синхронизации, доступные на С#. Некоторые примеры доступных объектов синхронизации: System.Threading.Mutex, System.Threading.Semaphore, System.Threading.ManualResetEvent и System.Threading.AutoResetEvent (это не исчерпывающий список, но это основы). Точный объект синхронизации, который вы хотите, зависит от ваших конкретных потребностей.

Мьютекс, вероятно, самый простой в использовании, поэтому я приведу его в качестве примера. Скажем, у меня есть две функции, которые работают в двух разных потоках (я буду придерживаться ваших собственных примеров, proc1 и proc2). Обе функции должны получить доступ к одной и той же переменной, foo. В каждой функции, которая обращается к foo, вам нужно сначала заблокировать ваш мьютекс. Затем сделайте все, что вам нужно, затем откройте его.

Например:

class bar
{
  private int foo;
  private System.Threading.Mutex lock = new System.Threading.Mutex;

  public void proc1()
  {
    lock.WaitOne();

    // do stuff to foo

    lock.ReleaseMutex();
  }

  public void proc2()
  {
    lock.WaitOne();

    // do stuff to foo

    lock.ReleaseMutex();
  }
};

С помощью этого метода будет выполняться proc1. Он попытается "схватить" мьютекс (замок). Если мьютекс уже захвачен, proc1 перейдет в спящий режим, пока мьютекс не будет "освобожден" другим потоком - proc1 будет сидеть и ждать, ничего не делать (и не есть циклов процессора!) До тех пор, пока мьютекс не будет выпущен. Затем он закроет его и сделает свое дело. Никакая другая нить не сможет захватить мьютекс до тех пор, пока proc1 не будет выполнен с ней.

Другой вариант - использовать событие. С# предоставляет два типа событий - ручной reset и авторешетку. Для моего примера я буду использовать руководство reset.

class bar
{
  private System.Threading.ManualResetEvent event = new System.Threading.ManualResetEvent;

  public void proc1()
  {
    // do stuff

    event.Set();
  }

  public void proc2()
  {
    event.WaitOne();
    event.Reset();

    // do stuff
  }
};

В этом примере, когда proc2 работает, он переходит в режим сна, когда он нажимает "event.WaitOne" и не использует циклы процессора, пока proc1 "не устанавливает" событие. Установка события заставляет proc2 просыпаться, и теперь он может сделать свое дело. Поскольку это ручной reset событие, событие будет оставаться в состоянии "set" до вызова event.Reset().

  • 0
    Я бы сказал, что «базовый» объект синхронизации в .NET - это монитор, на самом деле ... нет необходимости входить в объекты Win32, если вам действительно не нужно :)
0

В зависимости от характера многопоточности Thread.Join может сделать трюк. Он блокирует вызывающий поток, пока поток не завершится, продолжая выполнять стандартную передачу сообщений.

0

Reset события особенно удобны для этого.

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx

Ещё вопросы

Сообщество Overcoder
Наверх
Меню