I have some "FreeOnTerminate
" worker threads which add their handles on a TThreadList
when they start executing and remove from the same when their execution ends. They also check on a global event object that would notify them to cancel their work.
Following is the part that runs in the main thread which signals the event and waits for possible worker threads to end. WorkerHandleList
is the global ThreadList
.
...
procedure WaitForWorkers;
var
ThreadHandleList: TList;
begin
ThreadHandleList := TWorkerThread.WorkerHandleList.LockList;
TWorkerThread.WorkerHandleList.UnlockList;
WaitForMultipleObjects(ThreadHandleList.Count,
PWOHandleArray(ThreadHandleList.List), True, INFINITE);
end;
initialization
TWorkerThread.RecallAllWorkers := TEvent.Create;
TWorkerThread.WorkerHandleList := TThreadList.Create;
finalization
TWorkerThread.RecallAllWorkers.SetEvent;
WaitForWorkers;
TWorkerThread.RecallAllWorkers.Free;
TWorkerThread.WorkerHandleList.Free;
This design, I think, has a flaw in that I have to unlock the list just before waiting on the threads' handles because that would cause a deadlock since the threads themselves remove their handles from the same list. Without any lock, a context switch could cause a thread to free itself causing WaitForMultipleObjects
to return immediately with WAIT_FAILED
. I can't employ another lock either since WaitForMultipleObjects
is blocking and I wouldn't be able to release the lock from the main thread.
I can modify this design in a number ways including not using FreeOnTerminate
threads, which would guarantee valid handles until they are explicitly freed. Or modifying the list of thread handles only from the main thread. Or probably others...
But what I want to ask is, is there a solution to this kind of problem without changing the design? For instance, would sleeping in worker thread code before they remove their handles from the list, or calling SwitchToThread
cause all non-worker threads have a run? Enough run?