Jestem młodszym programistą pracującym nad pisaniem aktualizacji oprogramowania, które odbiera dane z rozwiązania innej firmy, przechowuje je w bazie danych, a następnie warunkuje dane do wykorzystania przez inne rozwiązanie innej firmy. Nasze oprogramowanie działa jako usługa systemu Windows.
Patrząc na kod z poprzedniej wersji, widzę to:
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);
}
Logika wydaje się jasna: poczekaj na miejsce w puli wątków, upewnij się, że usługa nie została zatrzymana, a następnie zwiększ licznik wątków i ustaw kolejkę w pracy. _runningWorkersjest zmniejszana wewnątrz SomeMethod()wewnątrz lockoświadczenie, że wywołuje Monitor.Pulse(_workerLocker).
Moje pytanie brzmi:
czy jest jakaś korzyść z grupowania całego kodu w jednym lock, tak jak to:
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;
}
}
}
Wygląda na to, że może to wymagać nieco więcej czasu na inne wątki, ale wydaje się, że wielokrotne blokowanie w jednym bloku logicznym byłoby również trochę czasochłonne. Jednak jestem nowy w wielowątkowości, więc zakładam, że istnieją inne obawy, których nie jestem świadomy.
Jedyne inne miejsca, w których _workerLockerzostanie zablokowany SomeMethod(), znajdują się tylko w celu zmniejszenia _runningWorkers, a następnie na zewnątrz, foreachaby poczekać, aż liczba _runningWorkersspadnie do zera przed zalogowaniem i powrotem.
Dziękuję za wszelką pomoc.
EDYCJA 4/8/15
Dzięki @delnan za zalecenie użycia semafora. Kod staje się:
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()nazywa się wewnątrz SomeMethod().
SomeMethodasynchronicznie, powyższa sekcja „blokady” zostanie pozostawiona przed lub przynajmniej wkrótce po uruchomieniu nowego wątku z SomeMethod.
Monitor.Wait()jest zwolnienie i ponowne uzyskanie blokady, aby inny zasób ( SomeMethodw tym przypadku) mógł z niej korzystać. Z drugiej strony SomeMethoduzyskuje blokadę, zmniejsza licznik, a następnie wywołuje, Monitor.Pulse()co powoduje powrót blokady do danej metody. Ponownie, to jest moje własne zrozumienie.
Monitor.Waitzwalnia blokadę. Polecam zajrzeć do dokumentacji.