Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 如何在线程delphi的Execute中使用Dictionary/StringList_Multithreading_Delphi_Dictionary - Fatal编程技术网

Multithreading 如何在线程delphi的Execute中使用Dictionary/StringList

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

我有一个线程类TValidateInvoiceThread:

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;