Kaynağa Gözat

chg: run work items inline if there is likely no thread left

This needs separate testing; TODO left in there until that happens
master
Martok 11 yıl önce
ebeveyn
işleme
1165916326
1 değiştirilmiş dosya ile 63 ekleme ve 23 silme
  1. +63
    -23
      uutlThreads.pas

+ 63
- 23
uutlThreads.pas Dosyayı Görüntüle

@@ -84,6 +84,7 @@ type
constructor Create(Owner: TutlThreadPool); constructor Create(Owner: TutlThreadPool);
destructor Destroy; override; destructor Destroy; override;
procedure Execute; override; procedure Execute; override;
procedure BlockedByQueueWait(const IsBlocked: boolean);
end; end;


TWorkerThreadList = specialize TutlList<TThread>; TWorkerThreadList = specialize TutlList<TThread>;
@@ -91,6 +92,7 @@ type
private private
fTerminating: Boolean; fTerminating: Boolean;
fThreads: TWorkerThreadList; fThreads: TWorkerThreadList;
fUnblockedThreads: LongInt;
fThreadMgmtSection: TCriticalSection; fThreadMgmtSection: TCriticalSection;
fQueue: TWorkItemList; fQueue: TWorkItemList;
fQueueSection: TutlSpinLock; fQueueSection: TutlSpinLock;
@@ -102,7 +104,7 @@ type
function FetchWork: TQueueItem; function FetchWork: TQueueItem;
procedure ThreadWaitForEvent; procedure ThreadWaitForEvent;
public public
constructor Create;
constructor Create(const ThreadCount: integer = 0);
destructor Destroy; override; destructor Destroy; override;
// TODO: attention: if tasks queue more tasks and then wait for them, recursing more than MaxThreads levels is a deadlock ATM. This will be fixed later // TODO: attention: if tasks queue more tasks and then wait for them, recursing more than MaxThreads levels is a deadlock ATM. This will be fixed later
property MaxThreads: integer read GetMaxThreads write SetMaxThreads; property MaxThreads: integer read GetMaxThreads write SetMaxThreads;
@@ -168,7 +170,7 @@ end;


{ TutlThreadPool } { TutlThreadPool }


constructor TutlThreadPool.Create;
constructor TutlThreadPool.Create(const ThreadCount: integer);
begin begin
inherited Create; inherited Create;
fTerminating:= false; fTerminating:= false;
@@ -177,7 +179,11 @@ begin
fThreads:= TWorkerThreadList.Create(true); fThreads:= TWorkerThreadList.Create(true);
fThreadMgmtSection:= TCriticalSection.Create; fThreadMgmtSection:= TCriticalSection.Create;
fNewItemEvent:= TEvent.Create(nil, false, true, ''); fNewItemEvent:= TEvent.Create(nil, false, true, '');
MaxThreads:= 2;
fUnblockedThreads:= 0;
if ThreadCount = 0 then
MaxThreads:= TThread.ProcessorCount + 2
else
MaxThreads:= ThreadCount;
end; end;


destructor TutlThreadPool.Destroy; destructor TutlThreadPool.Destroy;
@@ -205,11 +211,13 @@ begin
if MaxThreads=AValue then Exit; if MaxThreads=AValue then Exit;
if AValue < MaxThreads then begin if AValue < MaxThreads then begin
for i:= MaxThreads - 1 downto AValue do begin for i:= MaxThreads - 1 downto AValue do begin
InterLockedDecrement(fUnblockedThreads);
fThreads.Delete(i); // frees the item, which causes the thread to Terminate, Waitfor and Destroy fThreads.Delete(i); // frees the item, which causes the thread to Terminate, Waitfor and Destroy
end; end;
end else begin end else begin
for i:= MaxThreads to AValue - 1 do begin for i:= MaxThreads to AValue - 1 do begin
fThreads.Add(TWorker.Create(Self)); fThreads.Add(TWorker.Create(Self));
InterLockedIncrement(fUnblockedThreads);
end; end;
end; end;
finally finally
@@ -221,30 +229,33 @@ procedure TutlThreadPool.Shutdown;
var var
i: integer; i: integer;
begin begin
// don't give out any new Tasks
fTerminating:= true; fTerminating:= true;
// kill all threads
fThreadMgmtSection.Enter; fThreadMgmtSection.Enter;
try try
// kill all threads
for i:= 0 to fThreads.Count - 1 do for i:= 0 to fThreads.Count - 1 do
fThreads[i].Terminate; fThreads[i].Terminate;
// some thready may be waiting for FetchWork or a Task that they recursively spawned
fNewItemEvent.SetEvent; fNewItemEvent.SetEvent;
// kill all remaining tasks and notifiy Waitables
fQueueSection.Enter;
try
for i:= 0 to fQueue.Count - 1 do begin
fQueue[i].Cancel;
fQueue[i]._Release;
end;
fQueue.Clear;
finally
fQueueSection.Leave;
end;
// every possible event is set now, wait for workers to finish
for i:= 0 to fThreads.Count - 1 do for i:= 0 to fThreads.Count - 1 do
fThreads[i].WaitFor; fThreads[i].WaitFor;
fThreads.Clear; fThreads.Clear;
finally finally
fThreadMgmtSection.Leave; fThreadMgmtSection.Leave;
end; end;
// kill all remaining tasks and notifiy Waitables
fQueueSection.Enter;
try
for i:= 0 to fQueue.Count - 1 do begin
fQueue[i].Cancel;
fQueue[i]._Release;
end;
fQueue.Clear;
finally
fQueueSection.Leave;
end;
end; end;


function TutlThreadPool.Queue(const Task: IutlThreadPoolRunnable): IutlThreadPoolWaitable; function TutlThreadPool.Queue(const Task: IutlThreadPoolRunnable): IutlThreadPoolWaitable;
@@ -256,13 +267,21 @@ begin


itm:= TQueueItem.Create(Task); itm:= TQueueItem.Create(Task);
Result:= itm; Result:= itm;
fQueueSection.Enter;
try
itm._AddRef;
fQueue.Add(itm);
fNewItemEvent.SetEvent;
finally
fQueueSection.Leave;
// do we have a thread, or is everyone waiting for a task?
if InterlockedCompareExchange(fUnblockedThreads, 0, 0) = 0 then begin
// waiting for task, prevent deadlock by running this Task in the current thread
itm.Execute;
// Result has 1 Ref, so the caller frees the QueueItem
end else begin
// normal operation
fQueueSection.Enter;
try
itm._AddRef;
fQueue.Add(itm);
fNewItemEvent.SetEvent;
finally
fQueueSection.Leave;
end;
end; end;
end; end;


@@ -330,6 +349,14 @@ begin
end; end;
end; end;


procedure TutlThreadPool.TWorker.BlockedByQueueWait(const IsBlocked: boolean);
begin
if IsBlocked then
InterLockedDecrement(fOwner.fUnblockedThreads)
else
InterLockedIncrement(fOwner.fUnblockedThreads);
end;

{ TutlThreadPool.TQueueItem } { TutlThreadPool.TQueueItem }


constructor TutlThreadPool.TQueueItem.Create(const aTask: IutlThreadPoolRunnable); constructor TutlThreadPool.TQueueItem.Create(const aTask: IutlThreadPoolRunnable);
@@ -346,8 +373,21 @@ begin
end; end;


function TutlThreadPool.TQueueItem.WaitFor: TWaitResult; function TutlThreadPool.TQueueItem.WaitFor: TWaitResult;
var
ct: TThread;
ctw: TutlThreadPool.TWorker;
begin begin
Result:= fDoneEvent.WaitFor(INFINITE);
ct:= TThread.CurrentThread;
if ct is TutlThreadPool.TWorker then begin
ctw:= TutlThreadPool.TWorker(ct);
ctw.BlockedByQueueWait(true);
try
Result:= fDoneEvent.WaitFor(INFINITE);
finally
ctw.BlockedByQueueWait(false);
end;
end else
Result:= fDoneEvent.WaitFor(INFINITE);
end; end;


procedure TutlThreadPool.TQueueItem.Execute; procedure TutlThreadPool.TQueueItem.Execute;


Yükleniyor…
İptal
Kaydet