इससे कोई फर्क नहीं पड़ता कि आइटम TSparseArray<T>
पर लिखे गए हैं, क्योंकि केवल तभी आवश्यक है जब एक कार्यकर्ता थ्रेड ने उन्हें दिए गए सभी कार्यों को समाप्त कर दिया हो और अन्य कार्यकर्ता धागा अभी तक समाप्त नहीं हुआ है। इस बिंदु पर निष्क्रिय धागा पूल के अंदर के अन्य हिस्सों की कतारों को देख रहा है और कुछ काम चुरा लेने की कोशिश करता है।
यदि कोई कतार इस सरणी में नहीं आई है तो निष्क्रिय धागे को दिखाई नहीं दे रहा है और इसलिए काम करने वाले लोड को साझा नहीं किया जा सकता है।
कि मैं विकल्प 2
function TSparseArray<T>.Add(const Item: T): Integer;
...
SetLength(NewArray, Length(LArray) * 2);
TArray.Copy<T>(LArray, NewArray, I + 1); // <- No Exception here with XE7U1
NewArray[I + 1] := Item;
{$IFDEF USE_BUGFIX}
FArray := NewArray;
{$ENDIF}
Exit(I + 1);
चयन ठीक करने के लिए लेकिन वह हिस्सा चोरी जोखिम भरा किसी भी ताला लगा
procedure TThreadPool.TQueueWorkerThread.Execute;
...
if Signaled then
begin
I := 0;
while I < Length(ThreadPool.FQueues.Current) do
begin
if (ThreadPool.FQueues.Current[I] <> nil)
and (ThreadPool.FQueues.Current[I] <> WorkQueue)
and ThreadPool.FQueues.Current[I].TrySteal(Item)
then
Break;
Inc(I);
end;
if I <> Length(ThreadPool.FQueues.Current) then
Break;
LookedForSteals := True;
end
सरणी लंबाई केवल इतना बढ़ रहा है
while I < Length(ThreadPool.FQueues.Current) do
बिना लागू किया है
और
if I <> Length(ThreadPool.FQueues.Current) then
पर्याप्त सुरक्षित होना चाहिए।
if Signaled then
begin
I := 0;
while I < Length(ThreadPool.FQueues.Current) do
begin
{$IFDEF USE_BUGFIX}
TMonitor.Enter(ThreadPool.FQueues);
try
{$ENDIF}
if (ThreadPool.FQueues.Current[I] <> nil) and (ThreadPool.FQueues.Current[I] <> WorkQueue) and ThreadPool.FQueues.Current[I].TrySteal(Item) then
Break;
{$IFDEF USE_BUGFIX}
finally
TMonitor.Exit(ThreadPool.FQueues);
end;
{$ENDIF}
Inc(I);
end;
if I <> Length(ThreadPool.FQueues.Current) then
Break;
LookedForSteals := True;
end
अब हम एक परीक्षण वातावरण की जरूरत है चोरी को देखने के लिए:
program WatchStealingTasks;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Winapi.Windows,
System.SysUtils,
System.Threading,
System.Classes,
System.Math;
procedure OutputDebugStr(const AStr: string); overload;
begin
OutputDebugString(PChar(AStr));
end;
procedure OutputDebugStr(const AFormat: string; const AParams: array of const); overload;
begin
OutputDebugStr(Format(AFormat, AParams));
end;
function CreateInnerTask(AThreadId: Cardinal; AValue: Integer; APool: TThreadPool): ITask;
begin
Result := TTask.Run(
procedure
begin
Sleep(AValue);
if AThreadId <> TThread.CurrentThread.ThreadID
then
OutputDebugStr('[%d] executed stolen task from [%d]', [TThread.CurrentThread.ThreadID, AThreadId])
else
OutputDebugStr('[%d] executed task', [TThread.CurrentThread.ThreadID]);
end, APool);
end;
function CreateTask(AValue: Integer; APool: TThreadPool): ITask;
begin
Result := TTask.Run(
procedure
var
LIdx: Integer;
LTasks: TArray<ITask>;
begin
// Create three inner tasks per task
SetLength(LTasks, 3);
for LIdx := Low(LTasks) to High(LTasks) do
begin
LTasks[LIdx] := CreateInnerTask(TThread.CurrentThread.ThreadID, AValue, APool);
end;
OutputDebugStr('[%d] waiting for tasks completion', [TThread.CurrentThread.ThreadID]);
TTask.WaitForAll(LTasks);
OutputDebugStr('[%d] task finished', [TThread.CurrentThread.ThreadID]);
end, APool);
end;
procedure Test;
var
LPool: TThreadPool;
LIdx: Integer;
LTasks: TArray<ITask>;
begin
OutputDebugStr('Test started');
try
LPool := TThreadPool.Create;
try
// Create three tasks
SetLength(LTasks, 3);
for LIdx := Low(LTasks) to High(LTasks) do
begin
// Let's put some heavy work (200ms) on the first tasks shoulder
// and the other tasks just some light work (20ms) to do
LTasks[LIdx] := CreateTask(IfThen(LIdx = 0, 200, 20), LPool);
end;
TTask.WaitForAll(LTasks);
finally
LPool.Free;
end;
finally
OutputDebugStr('Test completed');
end;
end;
begin
try
Test;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
और डीबग लॉग
Debug-Ausgabe: Test started Prozess WatchStealingTasks.exe (4532)
Thread-Start: Thread-ID: 2104. Prozess WatchStealingTasks.exe (4532)
Thread-Start: Thread-ID: 2188. Prozess WatchStealingTasks.exe (4532)
Thread-Start: Thread-ID: 4948. Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] waiting for tasks completion Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2104] waiting for tasks completion Prozess WatchStealingTasks.exe (4532)
Thread-Start: Thread-ID: 2212. Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] waiting for tasks completion Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] task finished Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] task finished Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2104] executed task Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2188] executed stolen task from [2104] Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [4948] executed stolen task from [2104] Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: [2104] task finished Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: Thread Exiting: 2188 Prozess WatchStealingTasks.exe (4532)
Debug-Ausgabe: Thread Exiting: 4948 Prozess WatchStealingTasks.exe (4532)
Thread-Ende: Thread-ID: 4948. Prozess WatchStealingTasks.exe (4532)
Thread-Ende: Thread-ID: 2188. Prozess WatchStealingTasks.exe (4532)
Thread-Ende: Thread-ID: 2212. Prozess WatchStealingTasks.exe (4532)
ठीक है, चोरी कार्यकर्ता धागे के किसी भी संख्या के साथ अब काम करना चाहिए , तो सब कुछ ठीक है?
नहीं
इस छोटे से परीक्षण आवेदन का अंत आ नहीं होगा, क्योंकि अब यह थ्रेड पूल का नाशक के अंदर जमा। पिछले कार्यकर्ता धागा
procedure TThreadPool.TQueueWorkerThread.Execute;
...
if ThreadPool.FWorkerThreadCount = 1 then
begin
// it is the last thread after all tasks executed, but
// FQueuedRequestCount is still on 7 - WTF
if ThreadPool.FQueuedRequestCount = 0 then
begin
एक और बग की वजह से समाप्त नहीं होगा यहाँ ठीक करने के लिए ... क्योंकि जब तब कार्य अब आप के लिए इंतजार कर रहे हैं के सभी Task.WaitForAll
के साथ कार्य के लिए इंतज़ार कर, आंतरिक रूप से फांसी दे दी गई लेकिन कमी नहीं होगी FQueuedRequestCount
।
फिक्सिंग कि
function TThreadPool.TryRemoveWorkItem(const WorkerData: IThreadPoolWorkItem): Boolean;
begin
Result := (QueueThread <> nil) and (QueueThread.WorkQueue <> nil);
if Result then
Result := QueueThread.WorkQueue.LocalFindAndRemove(WorkerData);
{$IFDEF USE_BUGFIX}
if Result then
DecWorkRequestCount;
{$ENDIF}
end;
और अब यह चलाता है जैसे कि यह एक ही बार में किया जाना चाहिए।
अद्यतन
रूप उवे द्वारा एक टिप्पणी हम भी परीक्षण के लिए तय System.Generics.Collections.TArray.Copy<T>
class procedure TArray.Copy<T>(const Source, Destination: array of T; SourceIndex, DestIndex, Count: NativeInt);
{$IFDEF USE_BUGFIX}
begin
CheckArrays(Pointer(@Source[0]), Pointer(@Destination[0]), SourceIndex, Length(Source), DestIndex, Length(Destination), Count);
if IsManagedType(T) then
System.CopyArray(Pointer(@Destination[DestIndex]), Pointer(@Source[SourceIndex]), TypeInfo(T), Count)
else
System.Move(Pointer(@Source[SourceIndex])^,Pointer(@Destination[DestIndex])^, Count * SizeOf(T));
end;
{$ELSE}
begin
CheckArrays(Pointer(@Source[0]), Pointer(@Destination[0]), SourceIndex, Length(Source), DestIndex, Length(Destination), Count);
if IsManagedType(T) then
System.CopyArray(Pointer(@Destination[SourceIndex]), Pointer(@Source[SourceIndex]), TypeInfo(T), Count)
else
System.Move(Pointer(@Destination[SourceIndex])^, Pointer(@Source[SourceIndex])^, Count * SizeOf(T));
end;
{$ENDIF}
एक साधारण जांच को ठीक करने की जरूरत है:
procedure TestArrayCopy;
var
LArr1, LArr2: TArray<Integer>;
begin
LArr1 := TArray<Integer>.Create(10, 11, 12, 13);
LArr2 := TArray<Integer>.Create(20, 21);
// copy the last 2 elements from LArr1 to LArr2
TArray.Copy<Integer>(LArr1, LArr2, 2, 0, 2);
end;
- XE7 साथ आप XE7 Update1 साथ एक अपवाद
- मिल जाएगा आप
LArr1 = (10, 11, 0, 0)
LArr2 = (20, 21)
उस के साथ
- मिल ऊपर समस्याओं का समाधान कर
LArr1 = (10, 11, 12, 13)
LArr2 = (12, 13)
यहां तक कि अगर 'TArray.Copy()' नियत ना मुझे लगता है कि होगा मिल जाएगा होगा 'फेयर्रे' का लापता असाइनमेंट/संशोधन एक अलग बग होगा जिसे भी ठीक करने की आवश्यकता है। अन्यथा सरणी में 'आइटम' जोड़ना 'जोड़ें') कैसे है? मैंने अभी तक 'TSparseArray' के स्रोत कोड को नहीं देखा है। –
'TSparseArray' अजीब है। अलग लॉक ऑब्जेक्ट बनाने की आवश्यकता क्यों महसूस होती है? खुद को लॉक करने में क्या गड़बड़ है? –