Multithreading 使用什么代替TThread.Suspend()

Multithreading 使用什么代替TThread.Suspend(),multithreading,c++builder,Multithreading,C++builder,假设我有一个工作线程,它填充主线程中声明的一个大向量。当工作线程仍在运行(响应用户交互)时,我希望主线程检查向量是否已填充到特定大小。如果有,我希望它从向量中提取一些值。如果没有,我希望它等待工作线程填充到所需的大小 由于工作线程可能仍在向向量添加项目(可能会导致调整大小/移动),我认为我只能在工作线程挂起但TThread.Suspend()不推荐时执行此操作。我花了好几天的时间研究了TMutex、TSemaphore等,但文档非常糟糕。谁能给我指一下正确的方向吗 一种可能的解决方案是在工作线程

假设我有一个工作线程,它填充主线程中声明的一个大向量。当工作线程仍在运行(响应用户交互)时,我希望主线程检查向量是否已填充到特定大小。如果有,我希望它从向量中提取一些值。如果没有,我希望它等待工作线程填充到所需的大小

由于工作线程可能仍在向向量添加项目(可能会导致调整大小/移动),我认为我只能在工作线程挂起但TThread.Suspend()不推荐时执行此操作。我花了好几天的时间研究了TMutex、TSemaphore等,但文档非常糟糕。谁能给我指一下正确的方向吗

一种可能的解决方案是在工作线程中填充一个单独的较小的向量,然后使用synchronize将其附加到较大的向量(等等),但我希望避免这种情况

由于工作线程可能仍在向向量添加项目(可能导致调整大小/移动),我认为我只能在工作线程挂起时执行此操作

这是一个非常好的主意

但不推荐使用TThread.Suspend()

即使它没有被弃用,使用它仍然是危险的。只有调试器才应该挂起线程,这就是
SuspendThread()
API的目的

我花了好几天的时间研究了TMutex、TSemaphore等,但文档非常糟糕。谁能给我指一下正确的方向吗

您可以简单地用or包装对向量的所有访问,然后主线程和工作线程都可以在需要对向量执行任何操作时进入锁。例如:

type
  TMyThread = class(TThread)
  private
    FLock: TCriticalSection;
  protected
    procedure Execute; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure Lock;
    procedure Unlock;
  end;

constructor TMyThread.Create;
begin
  inherited Create(False);
  FLock := TCriticalSection.Create;
end;

destructor TMyThread.Destroy;
begin
  FLock.Free;
end;

procedure TMyThread.Lock;
begin
  FLock.Enter;
end;

procedure TMyThread.Unlock;
begin
  FLock.Leave;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    Lock;
    try
      // do something...
    finally
      Unlock;
    end;
  end;
end;
type
  TMyThread = class(TThread)
  private
    FPauseEvent: TEvent;
    FPausedEvent: TEvent;
    FResumeEvent: TEvent;
    procedure CheckForPause;
  protected
    procedure Execute; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure Pause;
    procedure Unpause;
  end;

constructor TMyThread.Create;
begin
  inherited Create(False);
  FPauseEvent := TEvent.Create(nil, True, False, '');
  FPausedEvent := TEvent.Create(nil, True, False, '');
  FResumeEvent := TEvent.Create(nil, True, True, '');
end;

destructor TMyThread.Destroy;
begin
  FPauseEvent.Free;
  FPausedEvent.Free;
  FResumeEvent.Free;
end;

procedure TMyThread.Pause;
begin
  FResumeEvent.ResetEvent;
  FPauseEvent.SetEvent;
  FPausedEvent.WaitFor(Infinite);
end;

procedure TMyThread.Unpause;
begin
  FPauseEvent.ResetEvent;
  FResumeEvent.SetEvent;
end;

procedure TMyThread.CheckForPause;
begin
  if FPauseEvent.WaitFor(0) = wrSignaled then
  begin
    FPausedEvent.SetEvent;
    FResumeEvent.WaitFor(Infinite);
    FPausedEvent.ResetEvent;
  end;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    CheckForPause;
    if Terminated then Exit;
    // do something...
  end;
end;

如果发现工作线程比主线程更多地访问向量,则可以考虑使用A或A代替.< /P> 或者,您可以使用一些对象向工作线程发出暂停和恢复的信号。然后,主线程可以向线程发出暂停信号并等待它真正暂停,然后访问向量并在完成后取消暂停线程。例如:

type
  TMyThread = class(TThread)
  private
    FLock: TCriticalSection;
  protected
    procedure Execute; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure Lock;
    procedure Unlock;
  end;

constructor TMyThread.Create;
begin
  inherited Create(False);
  FLock := TCriticalSection.Create;
end;

destructor TMyThread.Destroy;
begin
  FLock.Free;
end;

procedure TMyThread.Lock;
begin
  FLock.Enter;
end;

procedure TMyThread.Unlock;
begin
  FLock.Leave;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    Lock;
    try
      // do something...
    finally
      Unlock;
    end;
  end;
end;
type
  TMyThread = class(TThread)
  private
    FPauseEvent: TEvent;
    FPausedEvent: TEvent;
    FResumeEvent: TEvent;
    procedure CheckForPause;
  protected
    procedure Execute; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure Pause;
    procedure Unpause;
  end;

constructor TMyThread.Create;
begin
  inherited Create(False);
  FPauseEvent := TEvent.Create(nil, True, False, '');
  FPausedEvent := TEvent.Create(nil, True, False, '');
  FResumeEvent := TEvent.Create(nil, True, True, '');
end;

destructor TMyThread.Destroy;
begin
  FPauseEvent.Free;
  FPausedEvent.Free;
  FResumeEvent.Free;
end;

procedure TMyThread.Pause;
begin
  FResumeEvent.ResetEvent;
  FPauseEvent.SetEvent;
  FPausedEvent.WaitFor(Infinite);
end;

procedure TMyThread.Unpause;
begin
  FPauseEvent.ResetEvent;
  FResumeEvent.SetEvent;
end;

procedure TMyThread.CheckForPause;
begin
  if FPauseEvent.WaitFor(0) = wrSignaled then
  begin
    FPausedEvent.SetEvent;
    FResumeEvent.WaitFor(Infinite);
    FPausedEvent.ResetEvent;
  end;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    CheckForPause;
    if Terminated then Exit;
    // do something...
  end;
end;


下面是我的努力。它避免了雷米事件的复杂性和J.最后评论中提到的TCriticalSection的陷阱。那是假设它有效。看起来确实如此,但如果有人能帮我找到我可能掉进的陷阱,我将不胜感激

用户将看到一个TForm,其中包含一个名为VecNdx的TEdit,用户使用该TEdit输入他想要的向量值的索引,以及一个名为GetVecVal的TButton,单击该按钮时,会通过在名为VecVal的TLabel中打印VecNdx的向量值来响应

虽然向量值本身是由rand()函数生成的,但您可以将其视为单步执行查询结果集的结果,而查询结果集的大小直到最后一步之后才知道

.h file

#ifndef ThreadH
#define ThreadH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ComCtrls.hpp>
#include <vector>
#include <atomic>
//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published:    // IDE-managed Components
    TEdit *VecNdx; // user enters vector index
    TButton *GetVecVal; // retreives value for vector at index entered above
    TLabel *VecVal; // displays above value
    void __fastcall GetVecValClick(TObject *Sender);

private:    // User declarations
    class TPopulate : public TThread
    {
    private:
        TMainForm *Main;
        void __fastcall ShowPopulated(void);
        int Count;
        clock_t ThreadStart; // clock() when thread starts running
    protected:
        void __fastcall Execute();
    public:
        __fastcall TPopulate(TMainForm *Parent) : Main(Parent) {}
    } *Populate;

    int VecSize=-1; // updated only after Populate finishes
    std::vector<int> Vec;
    std::atomic<int> UserNdx=-1,UserVal,CountSoFar;

public:     // User declarations
    __fastcall TMainForm(TComponent* Owner);
    __fastcall ~TMainForm();
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif





.cpp file

#include <vcl.h>
#pragma hdrstop

#include "Thread.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner)
{
    Populate=new TPopulate(this);
}
//---------------------------------------------------------------------------
__fastcall TMainForm::~TMainForm()
{
    delete Populate;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::TPopulate::ShowPopulated(void)
{
    Main->Caption = (Terminated ? String("Terminated after ") : String(Count)+" values in ")
    +((clock()-ThreadStart)/CLOCKS_PER_SEC)+" secs";
    Main->VecSize=Count;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::TPopulate::Execute()
{
    ThreadStart=clock();
    const int Mx=100000000;
    Count=0;
    for (int u; !Terminated && Count<Mx;)
    {
        Main->Vec.push_back(rand() % Mx);
        Count++;
        if ((u = Main->UserNdx) != -1)
        {
            if (Count>u) Main->UserVal=Main->Vec[u];
            else Main->CountSoFar=Count;
            Main->UserNdx=-1;
        }
    }
    Synchronize(ShowPopulated);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::GetVecValClick(TObject *Sender)
{
    int Ndx=VecNdx->Text.ToIntDef(-1);
    if (Ndx<0 || (VecSize>=0 && Ndx>=VecSize)) throw Exception("Range Error");
    if (VecSize>=0) VecVal->Caption=Vec[Ndx];
    else
    {
        CountSoFar=0; // if Populate changes CountSoFar => Vec[UserNdx] not yet assigned
        UserNdx=Ndx;
        while (UserNdx!=-1); // Ensure Populate processes UserNdx
        VecVal->Caption = CountSoFar ? "Populated only to "+String(CountSoFar) : int(UserVal);
    }
}
//---------------------------------------------------------------------------
.h文件
#ifndef螺纹
#定义线程
//---------------------------------------------------------------------------
#包括
#包括
#包括
#包括
#包括
#包括
#包括
//---------------------------------------------------------------------------
类别:公共TForm
{
__已发布://IDE托管组件
TEdit*VecNdx;//用户输入向量索引
TButton*GetVecVal;//检索上面输入的索引处的向量值
TLabel*VecVal;//显示上述值
void uu fastcall GetVecValClick(TObject*发送方);
private://用户声明
类TPopulate:公共TThread
{
私人:
TMA*Main;
void uu快速呼叫显示填充(void);
整数计数;
clock\t ThreadStart;//线程开始运行时的clock()
受保护的:
void uu fastcall Execute();
公众:
__fastcall TPopulate(TMainForm*Parent):主(父){}
}*人口;
int VecSize=-1;//仅在填充完成后更新
std::Vec;
std::atomic UserNdx=-1,UserVal,CountSoFar;
public://用户声明
__fastcall TMainForm(t组件*所有者);
__fastcall~tmaninform();
};
//---------------------------------------------------------------------------
外部包TMainForm*main表单;
//---------------------------------------------------------------------------
#恩迪夫
.cpp文件
#包括
#布拉格语hdrstop
#包括“Thread.h”
//---------------------------------------------------------------------------
#pragma包(智能初始化)
#pragma资源“*.dfm”
TMainForm*main表单;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent*Owner):TForm(Owner)
{
填充=新的TPopulate(本);
}
//---------------------------------------------------------------------------
__fastcall TMainForm::~TMainForm()
{
删除并填充;
}
//---------------------------------------------------------------------------
void\uu fastcall TMainForm::TPopulate::ShowPopulated(void)
{
主->标题=(终止?字符串(“终止后”):字符串(计数)+“中的值”)
+((clock()-ThreadStart)/CLOCKS_PER_secs)+“secs”;
主->向量大小=计数;
}
//---------------------------------------------------------------------------
void _fastcall TMainForm::TPopulate::Execute()
{
ThreadStart=clock();
常数int Mx=100000000;
计数=0;
对于(int u;!终止和&CountVec.push_)(rand()%Mx);
计数++;
如果((u=Main->UserNdx)!=-1)
{
如果(Count>u)Main->UserVal=Main->Vec[u];
else Main->CountSoFar=Count;
Main->UserNdx=-1;
}
}
同步(显示填充);
}
//---------------------------------------------------------------------------
void\uu fastcall TMainForm::GetVecValClick(TObject*Sender)
{
int Ndx=VecNdx->Text.ToIntDef(-1);
如果(Ndx=0&&Ndx>=VecSize))抛出异常(“范围错误”);
如果(VecSize>=0)VecVal->Caption=Vec[Ndx];
其他的
{
CountSoFar=0;//如果填充更改计数