Я - младший разработчик, работающий над написанием обновления для программного обеспечения, которое получает данные от стороннего решения, сохраняет их в базе данных и затем обрабатывает данные для использования другим сторонним решением. Наше программное обеспечение работает как служба Windows.
Глядя на код из предыдущей версии, я вижу это:
static Object _workerLocker = new object();
static int _runningWorkers = 0;
int MaxSimultaneousThreads = 5;
foreach(int SomeObject in ListOfObjects)
{
lock (_workerLocker)
{
while (_runningWorkers >= MaxSimultaneousThreads)
{
Monitor.Wait(_workerLocker);
}
}
// check to see if the service has been stopped. If yes, then exit
if (this.IsRunning() == false)
{
break;
}
lock (_workerLocker)
{
_runningWorkers++;
}
ThreadPool.QueueUserWorkItem(SomeMethod, SomeObject);
}
Логика кажется ясной: дождитесь места в пуле потоков, убедитесь, что служба не была остановлена, затем увеличьте счетчик потоков и поставьте в очередь работу. _runningWorkers
уменьшается внутри SomeMethod()
внутри lock
оператора, который затем вызывает Monitor.Pulse(_workerLocker)
.
Мой вопрос:
есть ли какая-то польза от группировки всего кода внутри одного lock
, например:
static Object _workerLocker = new object();
static int _runningWorkers = 0;
int MaxSimultaneousThreads = 5;
foreach (int SomeObject in ListOfObjects)
{
// Is doing all the work inside a single lock better?
lock (_workerLocker)
{
// wait for room in ThreadPool
while (_runningWorkers >= MaxSimultaneousThreads)
{
Monitor.Wait(_workerLocker);
}
// check to see if the service has been stopped.
if (this.IsRunning())
{
ThreadPool.QueueUserWorkItem(SomeMethod, SomeObject);
_runningWorkers++;
}
else
{
break;
}
}
}
Кажется, что это может вызвать немного больше ожидания для других потоков, но тогда кажется, что повторная блокировка в одном логическом блоке также будет отнимать много времени. Тем не менее, я новичок в многопоточности, поэтому я предполагаю, что есть и другие проблемы, о которых я не знаю.
Единственные другие места, где _workerLocker
блокируется, находятся внутри SomeMethod()
, и только с целью уменьшения _runningWorkers
, а затем за пределами, foreach
чтобы ждать, пока число не станет _runningWorkers
равным нулю, прежде чем регистрироваться и возвращаться.
Спасибо за любую помощь.
РЕДАКТИРОВАТЬ 4/8/15
Спасибо @delnan за рекомендацию использовать семафор. Код становится:
static int MaxSimultaneousThreads = 5;
static Semaphore WorkerSem = new Semaphore(MaxSimultaneousThreads, MaxSimultaneousThreads);
foreach (int SomeObject in ListOfObjects)
{
// wait for an available thread
WorkerSem.WaitOne();
// check if the service has stopped
if (this.IsRunning())
{
ThreadPool.QueueUserWorkItem(SomeMethod, SomeObject);
}
else
{
break;
}
}
WorkerSem.Release()
называется внутри SomeMethod()
.
SomeMethod
асинхронно, приведенный выше раздел «блокировки» будет оставлен до или, по крайней мере, вскоре после SomeMethod
запуска нового потока с .
Monitor.Wait()
состоит в том, чтобы снять и повторно захватить блокировку, чтобы ее SomeMethod
мог использовать другой ресурс ( в данном случае). С другой стороны, SomeMethod
получает блокировку, уменьшает счетчик и затем вызывает, Monitor.Pulse()
который возвращает блокировку рассматриваемому методу. Опять же, это мое собственное понимание.
Monitor.Wait
снимает блокировку. Я рекомендую взглянуть на документы.