2016-09-07 16 views
11

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. 

Trả lời

21

Các WaitForMultipleObjects()documentation trạng thái:

Số lượng tối đa của đối tượng xử lý là MAXIMUM_WAIT_OBJECTS.

Giá trị MAXIMUM_WAIT_OBJECTS là 64 (được xác định trong winnt.h), vì vậy 100 tay cầm vượt quá giới hạn. Tuy nhiên, các tài liệu hướng dẫn cũng giải thích làm thế nào để vượt qua giới hạn đó:

Để chờ đợi hơn xử lý MAXIMUM_WAIT_OBJECTS, sử dụng một trong các phương pháp sau:

  • Tạo một thread để chờ đợi trên tay cầm MAXIMUM_WAIT_OBJECTS, sau đó chờ đợi trên thread đó cộng với các tay cầm khác. Sử dụng kỹ thuật này để chia tay cầm thành các nhóm MAXIMUM_WAIT_OBJECTS.

  • Call RegisterWaitForSingleObject để chờ trên mỗi tay cầm. Một chuỗi chờ đợi từ nhóm thread chờ đợi trên MAXIMUM_WAIT_OBJECTS đối tượng đã đăng ký và gán một chuỗi công nhân sau khi đối tượng được báo hiệu hoặc khoảng thời gian hết hạn hết hạn.

Xem câu trả lời cho this question cho một ví dụ về kỹ thuật đầu tiên.

+3

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. –

+0

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 :-) –

+1

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. –

Các vấn đề liên quan