假设我有一个工作线程填充主线程中声明的大型向量。当工作线程仍在运行时(响应用户交互),我希望主线程检查向量是否已填充到一定大小。如果有,我希望它从矢量中提取一些值。如果它不是我想让它等到工作线程填充到所需的大小。
由于工作线程仍然可以向向量添加项目(可能导致调整大小/移动)我认为我只能在工作线程被挂起但不推荐使用TThread.Suspend()时执行此操作。我花了几天时间看着TMutex,TSemaphore等,但文档很糟糕。有人能指出我正确的方向吗?
一种可能的解决方案是在工作线程中填充一个单独的较小向量,然后使用synchronize将其附加到大向量(等等),但我想避免这种情况。
答案 0 :(得分:0)
由于工作线程仍然可以向向量添加项目(可能导致调整大小/移动)我认为我只能在工作线程被挂起时执行此操作
这是一个非常好的主意。
但不推荐使用TThread.Suspend()。
即使它没有被弃用,使用起来仍然很危险。只有调试器应该挂起一个线程,这就是SuspendThread()
API的用途。
我花了几天时间看着TMutex,TSemaphore等,但文档很糟糕。有人能指出我正确的方向吗?
您可以使用TCriticalSection
或TMutex
简单地包含对向量的所有访问权限,然后主要和工作线程都可以在需要对向量执行任何操作时进入锁定。例如:
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;
MyThread.Lock;
try
if Vector.Size >= X then
begin
// do something ...
end;
finally
MyThread.Unlock;
end;
如果您发现工作线程比主线程更多地访问向量,您可以考虑改为使用TMultiReadExclusiveWriteSynchronizer
或SRW lock。
或者,您可以使用一些TEvent
个对象来通知工作线程何时暂停以及何时恢复。然后主线程可以通知线程暂停并等待它实际暂停,然后访问向量并在完成时取消线程。例如:
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;
MyThread.Pause;
try
if Vector.Size >= X then
begin
// do something ...
end;
finally
MyThread.Unpause;
end;
答案 1 :(得分:0)
我的努力如下。它避免了Remy的TEvent的复杂性以及J ..最后评论所指出的TCriticalSection的陷阱。这是假设它有效。它似乎确实如此,但如果有人能够找到我可能陷入的陷阱,我将感激不尽。
向用户呈现包含名为VecNdx的TEdit的TForm,用户用来输入他想要的矢量值的索引和一个名为GetVecVal的TButton,点击时,通过在TLabel中打印VecNdx的矢量值来响应叫做VecVal。
虽然矢量值本身是由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);
}
}
//---------------------------------------------------------------------------