Trong chương trình thử nghiệm sau, mỗi chuỗi thử nghiệm thêm xử lý của nó vào một số toàn cầu TThreadList
khi nó bắt đầu thực hiện và loại bỏ xử lý của nó khỏi cùng một danh sách khi việc thực thi sắp kết thúc.Tại sao WaitForMultipleObjects không thành công với nhiều xử lý luồng?
Vì mục đích thử nghiệm, thêm vào, mỗi chuỗi đảm bảo nó thêm tay cầm của nó trước khi chuỗi chính khóa danh sách (để nhân đôi tay cầm của chúng và bắt đầu đợi chúng kết thúc). Các chủ đề cũng đảm bảo rằng chúng không loại bỏ các chốt của chúng trước khi chủ đề chính khóa danh sách.
Chương trình thử nghiệm chạy tốt cho khoảng 50-60 chủ đề. Sau đó, cuộc gọi WaitForMultipleObjects
bắt đầu không thành công với WAIT_FAILED
, GetLastError
trả lại 87 (ERROR_INVALID_PARAMETER
). Hiện tại nó bắt đầu 100 chủ đề. Câu hỏi của tôi là, tôi đang làm gì sai?
Chương trình được biên dịch với XE2 - cập nhật 4, nền tảng đích 32 bit. Hộp kiểm tra là W7x64.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
windows,
sysutils,
classes,
syncobjs;
type
TTestThread = class(TThread)
private
FAckStarted: TEvent;
function GetAckHandle: THandle;
class var
ThreadList: TThreadList;
WaitEnd: THandle;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
property AckHandle: THandle read GetAckHandle;
end;
{.$DEFINE FREEONTERMINATE}
constructor TTestThread.Create;
begin
inherited Create(True);
FAckStarted := TEvent.Create;
{$IFDEF FREEONTERMINATE}
FreeOnTerminate := True;
{$ENDIF}
end;
destructor TTestThread.Destroy;
begin
FAckStarted.Free;
inherited;
end;
procedure TTestThread.Execute;
begin
// OutputDebugString(PChar(Format('%d starting -------------', [Handle])));
ThreadList.Add(Pointer(Handle));
FAckStarted.SetEvent;
NameThreadForDebugging(AnsiString(IntToStr(Handle)));
WaitForSingleObject(WaitEnd, INFINITE);
ThreadList.Remove(Pointer(Handle));
// OutputDebugString(PChar(Format('%d leaving -------------', [Handle])));
end;
function TTestThread.GetAckHandle: THandle;
begin
Result := FAckStarted.Handle;
end;
const
NumThreads = 100;
var
DeferThreadEnd: TEvent;
ThreadList: array of TThread;
i: Integer;
Thread: TTestThread;
WaitThreadStart: THandle;
LockList: TList;
LockListCount: Integer;
ThreadHandleArr: array of THandle;
WaitRet: DWORD;
begin
IsMultiThread := True;
ReportMemoryLeaksOnShutdown := True;
TTestThread.ThreadList := TThreadList.Create;
DeferThreadEnd := TEvent.Create;
TTestThread.WaitEnd := DeferThreadEnd.Handle;
SetLength(ThreadList, NumThreads);
for i := 0 to NumThreads - 1 do begin
Thread := TTestThread.Create;
ThreadList[i] := Thread;
WaitThreadStart := Thread.GetAckHandle;
Thread.Start;
WaitForSingleObject(WaitThreadStart, INFINITE);
end;
LockList := TTestThread.ThreadList.LockList;
LockListCount := LockList.Count;
SetLength(ThreadHandleArr, LockListCount);
for i := 0 to LockListCount - 1 do
{$IFDEF FREEONTERMINATE}
Win32Check(DuplicateHandle(GetCurrentProcess, THandle(LockList[i]),
GetCurrentProcess, @ThreadHandleArr[i], SYNCHRONIZE, True, 0));
{$ELSE}
ThreadHandleArr[i] := THandle(LockList[i]);
{$ENDIF}
TTestThread.ThreadList.UnlockList;
DeferThreadEnd.SetEvent;
if LockListCount > 0 then begin
Writeln('waiting for ', LockListCount, ' threads');
WaitRet := WaitForMultipleObjects(LockListCount,
PWOHandleArray(ThreadHandleArr), True, INFINITE);
case WaitRet of
WAIT_OBJECT_0: Writeln('wait success');
WAIT_FAILED: Writeln('wait fail:', SysErrorMessage(GetLastError));
end;
end;
for i := 0 to Length(ThreadList) - 1 do begin
{$IFDEF FREEONTERMINATE}
Win32Check(CloseHandle(ThreadHandleArr[i]));
{$ELSE}
ThreadList[i].Free;
{$ENDIF}
end;
DeferThreadEnd.Free;
TTestThread.ThreadList.Free;
Writeln('program end');
Readln;
end.
Ngoài ra còn có khả năng sử dụng biến thể của [phương pháp này] (http://tondrej.blogspot.com/2016/03/the-strange-limitation-of-64-threads.html): được liên kết gia tăng/giảm truy cập và một sự kiện duy nhất để chờ đợi. –
Cảm ơn bạn đã chỉnh sửa. Tôi cảm thấy một chút xấu lấy tín dụng cho một câu trả lời tốt hơn so với tôi :-) –
Cũng lưu ý rằng giới hạn đối tượng 64 thực sự là một giới hạn phía hạt nhân. Định nghĩa lại MAXIMUM_WAIT_OBJECTS hoặc thậm chí reimplementing phía userland của WaitForMultipleObjects sẽ không làm được gì nhiều. Bạn chỉ cần làm việc xung quanh giới hạn cụ thể này. –