gpt4 book ai didi

multithreading - 如何处理阻塞调用期间需要释放锁的情况?

转载 作者:行者123 更新时间:2023-12-03 14:43:08 25 4
gpt4 key购买 nike

我有一些“FreeOnTerminate”工作线程,它们在开始执行时将其句柄添加到TThreadList上,并在执行结束时从相同的句柄中删除。他们还检查全局事件对象,该对象会通知他们取消工作。

以下是在主线程中运行的部分,它发出事件信号并等待可能的工作线程结束。 WorkerHandleList 是全局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;


我认为这种设计有一个缺陷,因为我必须在等待线程句柄之前解锁列表,因为这会导致死锁,因为线程本身从同一列表中删除了它们的句柄。如果没有任何锁,上下文切换可能会导致线程释放自身,从而导致 WaitForMultipleObjects 立即返回并显示 WAIT_FAILED。我也无法使用另一个锁,因为 WaitForMultipleObjects 正在阻塞,并且我无法从主线程释放该锁。

我可以通过多种方式修改此设计,包括不使用 FreeOnTerminate 线程,这将保证句柄有效,直到它们被显式释放。或者仅从主线程修改线程句柄列表。或者可能是其他人......

但是我想问的是,有没有办法在不改变设计的情况下解决这种问题呢?例如,在从列表中删除句柄之前,是否会在工作线程代码中休眠,或者调用 SwitchToThread 导致所有非工作线程运行?跑够了吗?

最佳答案

您对 LockList() 的使用是错误且危险的。一旦调用 UnlockList()TList 就不再 protected ,并且会随着工作线程从列表中删除自身而被修改。这种情况可能会在您有机会调用 WaitForMultipleObjects() 之前发生,或者更糟糕的是在为其设置调用堆栈时

您需要做的是锁定列表,将句柄复制到本地数组,解锁列表,然后等待数组。不要直接等待 TList 本身。

procedure WaitForWorkers;
var
ThreadHandleList: TList;
ThreadHandleArr: array of THandle;
begin
ThreadHandleList := TWorkerThread.WorkerHandleList.LockList;
try
SetLength(ThreadHandleArr, ThreadHandleList.Count);
for I := 0 to ThreadHandleList.Count-1 do
ThreadHandleArr[i] := ThreadHandleList[i];
finally
TWorkerThread.WorkerHandleList.UnlockList;
end;

WaitForMultipleObjects(Length(ThreadHandleArr), PWOHandleArray(ThreadHandleArr), True, INFINITE);
end;

但是,即使这样也存在竞争条件。在实际进入 WaitForMultipleObjects() 之前,某些工作线程可能已经终止,从而销毁了它们的句柄。其余线程将在其运行时销毁其句柄。不管怎样,它都会失败。当您主动等待线程句柄时,您不能销毁它们。

FreeOnTerminate=True 只能安全地用于您启动然后甚至忘记存在的线程。当您仍然需要出于任何原因访问线程时,使用FreeOnTerminate=True是非常危险的(尤其是因为这个警告,TThread.WaitFor( )FreeOnTerminate=True 时往往会崩溃 - 线程句柄甚至 TThread 对象本身在仍在使用时就会被销毁!)。

您需要重新考虑您的等待策略。我可以想到一些替代方案:

  1. 根本不要使用WaitForMultipleObjects()。简单地定期重新锁定列表并检查它是否为空更安全,但效率较低:

    procedure WaitForWorkers;
    var
    ThreadHandleList: TList;
    begin
    repeat
    ThreadHandleList := TWorkerThread.WorkerHandleList.LockList;
    try
    if ThreadHandleList.Count = 0 then Exit;
    finally
    TWorkerThread.WorkerHandleList.UnlockList;
    end;
    Sleep(500);
    until False;
    end;
  2. 完全摆脱 WorkerHandleList 并使用信号量或互锁计数器来跟踪已创建且尚未销毁的线程数量。当信号量/计数器指示不再存在线程时退出等待。

  3. 像 Ken B 建议的那样,继续使用 WorkerHandleList,但等待手动重置事件,该事件在第一个线程添加到列表中时重置(在线程构造函数中执行此操作,而不是在线程构造函数中执行此操作)在 Execute() 中),并在从列表中删除最后一个线程时发出信号(在线程析构函数中执行此操作,而不是在 Execute()DoTerminate() 中) )。

关于multithreading - 如何处理阻塞调用期间需要释放锁的情况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39356076/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com