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. _runningWorkers
jest zmniejszana wewnątrz SomeMethod()
wewnątrz lock
oś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 _workerLocker
zostanie zablokowany SomeMethod()
, znajdują się tylko w celu zmniejszenia _runningWorkers
, a następnie na zewnątrz, foreach
aby poczekać, aż liczba _runningWorkers
spadnie 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()
.
SomeMethod
asynchronicznie, 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 ( SomeMethod
w tym przypadku) mógł z niej korzystać. Z drugiej strony SomeMethod
uzyskuje 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.Wait
zwalnia blokadę. Polecam zajrzeć do dokumentacji.