Multithreading 如何在线程delphi的Execute中使用Dictionary/StringList
我有一个线程类TValidateInvoiceThread:Multithreading 如何在线程delphi的Execute中使用Dictionary/StringList,multithreading,delphi,dictionary,Multithreading,Delphi,Dictionary,我有一个线程类TValidateInvoiceThread: type TValidateInvoiceThread = class(TThread) private FData: TValidationData; FInvoice: TInvoice; // Do NOT free FPreProcessing: Boolean; procedure ValidateInvoice; protected proc
type
TValidateInvoiceThread = class(TThread)
private
FData: TValidationData;
FInvoice: TInvoice; // Do NOT free
FPreProcessing: Boolean;
procedure ValidateInvoice;
protected
procedure Execute; override;
public
constructor Create(const objData: TValidationData; const bPreProcessing: Boolean);
destructor Destroy; override;
end;
constructor TValidateInvoiceThread.Create(const objData: TValidationData;
const bPreProcessing: Boolean);
var
objValidatorCache: TValidationCache;
begin
inherited Create(False);
FData := objData;
objValidatorCache := FData.Caches.Items['TInvAccountValidator'];
end;
destructor TValidateInvoiceThread.Destroy;
begin
FreeAndNil(FData);
inherited;
end;
procedure TValidateInvoiceThread.Execute;
begin
inherited;
ValidateInvoice;
end;
procedure TValidateInvoiceThread.ValidateInvoice;
var
objValidatorCache: TValidationCache;
begin
objValidatorCache := FData.Caches.Items['TInvAccountValidator'];
end;
我在另一个类中创建了这个线程
procedure TInvValidators.ValidateInvoiceUsingThread(
const nThreadIndex: Integer;
const objValidatorCaches: TObjectDictionary<String, TValidationCache>;
const nInvoiceIndex: Integer; const bUseThread, bPreProcessing: Boolean);
begin
objValidationData := TValidationData.Create(FConnection, FAllInvoices, FAllInvoices[nInvoiceIndex], bUseThread);
objValidationData.Caches := objValidatorCaches;
objThread := TValidateInvoiceThread.Create(objValidationData, bPreProcessing);
FThreadArray[nThreadIndex] := objThread;
FHandleArray[nThreadIndex]:= FThreadArray[nThreadIndex].Handle;
end;
请注意,我已经删除了一些代码,试图让它更易于遵循
问题是我的字典越来越坏了
如果我在构造函数中设置断点,一切正常
但是,在Execute方法的第一行中,字典现在已损坏
字典本身是类的全局变量
我需要做一些特殊的事情来允许我在线程中使用字典吗
我对字符串列表也有同样的问题
编辑-根据要求提供其他信息
TInvValidators包含我的字典
TInvValidators = class(TSTCListBase)
private
FThreadArray : Array[1..nMaxThreads] of TValidateInvoiceThread;
FHandleArray : Array[1..nMaxThreads] of THandle;
FThreadsRunning: Integer; // total number of supposedly running threads
FValidationList: TObjectDictionary<String, TObject>;
end;
procedure TInvValidators.Validate(
const Phase: TValidationPhase;
const objInvoices: TInvoices;
const ReValidate: TRevalidateInvoices;
const IDs: TList<Integer>;
const objConnection: TSTCConnection;
const ValidatorCount: Integer);
var
InvoiceIndex: Integer;
i : Integer;
rWait : Cardinal;
Flags: DWORD; // dummy variable used in a call to find out if a thread handle is valid
nThreadIndex: Integer;
procedure ValidateInvoiceRange(const nStartInvoiceID, nEndInvoiceID: Integer);
var
InvoiceIndex: Integer;
I: Integer;
begin
nThreadIndex := 1;
for InvoiceIndex := nStartInvoiceID - 1 to nEndInvoiceID - 1 do
begin
if InvoiceIndex >= objInvoices.Count then
Break;
objInvoice := objInvoices[InvoiceIndex];
ValidateInvoiceUsingThread(nThreadIndex, FValidatorCaches, InvoiceIndex, bUseThread, False);
Inc(nThreadIndex);
if nThreadIndex > nMaxThreads then
Break;
end;
FThreadsRunning := nMaxThreads;
repeat
rWait:= WaitForMultipleObjects(FThreadsRunning, @FHandleArray, True, 100);
case rWait of
// one of the threads satisfied the wait, remove its handle
WAIT_OBJECT_0..WAIT_OBJECT_0 + nMaxThreads - 1: RemoveHandle(rWait + 1);
// at least one handle has become invalid outside the wait call,
// or more than one thread finished during the previous wait,
// find and remove them
WAIT_FAILED:
begin
if GetLastError = ERROR_INVALID_HANDLE then
begin
for i := FThreadsRunning downto 1 do
if not GetHandleInformation(FHandleArray[i], Flags) then // is handle valid?
RemoveHandle(i);
end
else
// the wait failed because of something other than an invalid handle
RaiseLastOSError;
end;
// all remaining threads continue running, process messages and loop.
// don't process messages if the wait returned WAIT_FAILED since we didn't wait at all
// likewise WAIT_OBJECT_... may return soon
WAIT_TIMEOUT: Application.ProcessMessages;
end;
until FThreadsRunning = 0; // no more valid thread handles, we're done
end;
begin
try
FValidatorCaches := TObjectDictionary<String, TValidationCache>.Create([doOwnsValues]);
for nValidatorIndex := 0 to Count - 1 do
begin
objValidator := Items[nValidatorIndex];
objCache := TValidationCache.Create(objInvoices);
FValidatorCaches.Add(objValidator.ClassName, objCache);
objValidator.PrepareCache(objCache, FConnection, objInvoices[0].UtilityType);
end;
nStart := 1;
nEnd := nMaxThreads;
while nStart <= objInvoices.Count do
begin
ValidateInvoiceRange(nStart, nEnd);
Inc(nStart, nMaxThreads);
Inc(nEnd, nMaxThreads);
end;
finally
FreeAndNil(FMeterDetailCache);
end;
end;
TInvValidators=class(TSTCListBase)
私有的
FThreadArray:TValidateInvoiceThread的数组[1..nMaxThreads];
FHandleArray:THandle的数组[1..nMaxThreads];
FThreadsRunning:Integer;//假定正在运行的线程总数
FValidationList:TObjectDictionary;
结束;
程序TInvValidators.Validate(
常量阶段:TValidationPhase;
const objInvoices:TInvoices;
const ReValidate:TRevalidateInvoices;
const-id:TList;
常量对象连接:TSTCConnection;
常量验证器计数:整数);
变量
发票索引:整数;
i:整数;
鲁维特:红衣主教;
标志:DWORD;//在调用中使用的伪变量,用于确定线程句柄是否有效
nThreadIndex:整数;
过程ValidateInvoiceRange(const nStartInvoiceID,nEndInvoiceID:Integer);
变量
发票索引:整数;
I:整数;
开始
nThreadIndex:=1;
对于InvoiceIndex:=nStartInvoiceID-1到nEndInvoiceID-1 do
开始
如果InvoiceIndex>=objInvoices.Count,则
打破
objInvoice:=objInvoices[InvoiceIndex];
使用读取(nThread索引、FValidatorCaches、InvoiceIndex、bUseThread、False)验证发票;
公司(索引),;
如果nThreadIndex>nMaxThreads,则
打破
结束;
FThreadsRunning:=nMaxThreads;
重复
rWait:=WaitForMultipleObjects(FThreadsRunning,@FHandleArray,True,100);
卢维特案
//其中一个线程满足了等待,请移除其句柄
WAIT_OBJECT_0..WAIT_OBJECT_0+nMaxThreads-1:RemoveHandle(rWait+1);
//至少有一个句柄在等待调用之外变得无效,
//或者在上一次等待期间完成了多个线程,
//找到并移除它们
等待\u失败:
开始
如果GetLastError=ERROR\u无效\u句柄,则
开始
对于i:=FThreadsRunning down to 1 do
如果不是GetHandleInformation(FHandleArray[i],Flags),那么//句柄是否有效?
移除手柄(i);
结束
其他的
//等待失败,原因不是无效句柄
赖斯·塞罗;
结束;
//所有剩余线程继续运行,处理消息并循环。
//如果等待返回wait\u失败,则不要处理消息,因为我们根本没有等待
//同样,请等待\u对象\u。。。可能很快就会回来
等待超时:Application.ProcessMessages;
结束;
直到FThreadsRunning=0;//没有更多有效的线程句柄,我们完成了
结束;
开始
尝试
FValidatorCaches:=TObjectDictionary.Create([DoownsValue]);
对于nValidatorIndex:=0以计数-1 do
开始
objValidator:=项目[nValidatorIndex];
objCache:=TValidationCache.Create(objCache);
添加(objValidator.ClassName,objCache);
objValidator.PrepareCache(objCache,FConnection,objInvoices[0].UtilityType);
结束;
nStart:=1;
nEnd:=nMaxThreads;
在我就如何解决您的问题提供指导之前,我将给您一个非常重要的提示
在尝试使多线程实现正常工作之前,首先确保代码是单线程的。关键是多线程代码增加了一个全新的复杂性层。在代码在单个线程中正常工作之前,它不可能在多个线程中正常工作。而额外的一层复杂性使得修复非常困难
您可能认为您已经有了一个可以工作的单线程解决方案,但我发现您的代码中存在错误,这意味着您仍然有很多资源管理错误。下面是一个仅包含相关行的示例,以及解释错误的注释:
begin
try //try/finally is used for resource protection, in order to protect a
//resource correctly, it should be allocated **before** the try.
FValidatorCaches := TObjectDictionary<String, TValidationCache>.Create([doOwnsValues]);
finally
//However, in the finally you're destroying something completely
//different. In fact, there are no other references to FMeterDetailCache
//anywhere else in the code you've shown. This strongly implies an
//error in your resource protection.
FreeAndNil(FMeterDetailCache);
end;
end;
上面的要点是,很容易忘记主线程可能会在子线程开始运行之前就决定销毁字典
至于第二种可能性,这取决于TValidationData
的析构函数内部发生的情况。因为您没有显示该代码,所以只有您知道答案
调试以查明问题
假设字典被销毁得太快,进行一点调试可以快速确定字典被销毁的位置/原因。从您的问题来看,您似乎已经进行了一些调试,因此我假设您在执行以下步骤时不会遇到问题:
- 在字典的析构函数的第一行放置断点
- 运行你的代码
- 如果在到达字典的析构函数之前到达
Execute
,那么线程应该仍然能够使用字典
- 如果在到达
Execute
之前到达字典的析构函数,则只需检查导致对象销毁的调用序列
内存覆盖情况下的调试
对内存覆盖的可能性保持开放的心态。。。这是一个有点棘手的调试。但是,如果您能够始终如一地重现问题,则应该可以进行调试
- 在线程的析构函数中放置断点
- 运行应用程序
- 当您到达上述断点时,找到地址
begin
try //try/finally is used for resource protection, in order to protect a
//resource correctly, it should be allocated **before** the try.
FValidatorCaches := TObjectDictionary<String, TValidationCache>.Create([doOwnsValues]);
finally
//However, in the finally you're destroying something completely
//different. In fact, there are no other references to FMeterDetailCache
//anywhere else in the code you've shown. This strongly implies an
//error in your resource protection.
FreeAndNil(FMeterDetailCache);
end;
end;
Main Thread: ......C......D........
Child Thread ---------S......
. = code being executed
C = child thread created
- = child thread exists, but isn't doing anything yet
S = OS has started the child thread
D = main thread destroys dictionary
procedure TValidateInvoiceThread.Execute;
begin
inherited;
ValidateInvoice;
end;