Reputation: 1
I have some errors while compiling a C++Builder 6 threads project. I don't know how to fix them.
Unit1.cpp:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
using namespace std;
int i1 = 0, i2 = 0;
T1 *Thread1, *Thread2;
bool Term1, Term2;
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall T1::Execute()
{
do
Synchronize (UpdateTimer);
while (!Terminated);
}
//-----------------------------------------------
void __fastcall T1::UpdateTimer()
{
Timer->Enabled = !Terminated;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ComboBox1->ItemIndex = 3;
ComboBox2->ItemIndex = 3;
Thread1 = new T1(false);
Thread1->Timer = Timer1;
Thread1->FreeOnTerminate = true;
Term1 = false;
Thread2 = new T1(true);
Thread2->Timer = Timer2;
Thread2->FreeOnTerminate = true;
Term2 = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
i1++;
Label1->Caption = i1;
Timer1->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
i2++;
Label2->Caption = i2;
Timer2->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BRes1Click(TObject *Sender)
{
if (!Term1)
Thread1->Resume();
else ShowMessage("Ïîòîê óæå çàâåðøåí");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BRes2Click(TObject *Sender)
{
if (!Term2)
Thread2->Resume();
else ShowMessage("Ïîòîê óæå çàâåðøåí");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BTerm1Click(TObject *Sender)
{
Thread1->Terminate();
Term1 = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BTerm2Click(TObject *Sender)
{
Thread2->Terminate();
Term2 = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BSuspend1Click(TObject *Sender)
{
if (!Term1)
Thread1->Suspend();
else ShowMessage("Ïîòîê óæå çàâåðøåí");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BSuspend2Click(TObject *Sender)
{
if (!Term2)
Thread2->Suspend();
else ShowMessage("Ïîòîê óæå çàâåðøåí");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BSyncClick(TObject *Sender)
{
if (Term1 || Term2)
ShowMessage("Ïîòîê óæå çàâåðøåí");
else
{
if (!Thread1->Suspended)
Thread1->Suspend();
if (!Thread2->Suspended)
Thread2->Suspend();
Timer1->Enabled = false;
Timer2->Enabled = false;
i1 = 0;
i2 = 0;
Thread1->Resume();
Thread2->Resume();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
if (Term1)
ShowMessage("Ïîòîê óæå çàâåðøåí");
else
switch (ComboBox1->ItemIndex)
{
case 0: Thread1->Priority = tpIdle; break;
case 1: Thread1->Priority = tpLowest; break;
case 2: Thread1->Priority = tpLower; break;
case 3: Thread1->Priority = tpNormal; break;
case 4: Thread1->Priority = tpHigher; break;
case 5: Thread1->Priority = tpHighest; break;
case 6: Thread1->Priority = tpTimeCritical; break;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ComboBox2Change(TObject *Sender)
{
if (Term2)
ShowMessage("Ïîòîê óæå çàâåðøåí");
else
switch (ComboBox2->ItemIndex)
{
case 0: Thread2->Priority = tpIdle; break;
case 1: Thread2->Priority = tpLowest; break;
case 2: Thread2->Priority = tpLower; break;
case 3: Thread2->Priority = tpNormal; break;
case 4: Thread2->Priority = tpHigher; break;
case 5: Thread2->Priority = tpHighest; break;
case 6: Thread2->Priority = tpTimeCritical; break;
}
}
//---------------------------------------------------------------------------
Uni1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TGroupBox *GroupBox1;
TGroupBox *GroupBox2;
TLabel *Label1;
TButton *BRes1;
TButton *BSuspend1;
TButton *BTerm1;
TLabel *Label2;
TButton *BRes2;
TButton *BSuspend2;
TButton *BTerm2;
TComboBox *ComboBox1;
TComboBox *ComboBox2;
TButton *BSync;
TTimer *Timer1;
TTimer *Timer2;
void __fastcall FormCreate(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall Timer2Timer(TObject *Sender);
void __fastcall BRes1Click(TObject *Sender);
void __fastcall BRes2Click(TObject *Sender);
void __fastcall BTerm1Click(TObject *Sender);
void __fastcall BTerm2Click(TObject *Sender);
void __fastcall BSuspend1Click(TObject *Sender);
void __fastcall BSuspend2Click(TObject *Sender);
void __fastcall BSyncClick(TObject *Sender);
void __fastcall ComboBox1Change(TObject *Sender);
void __fastcall ComboBox2Change(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
class T1 : public TThread
{
private:
void _fastcall UpdateTimer();
protected:
void _fastcall Execute();
public:
__fastcall T1(bool CreateSuspended);
TTimer *Timer;
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Errors:
[Linker Error] Unresolved external 't1::' referenced from C:\USERS\ALEXEY\DESKTOP\CCC\UNIT1.OBJ
[Linker Error] Unresolved external '__fastcall T1::T1(bool)' referenced from C:\USERS\ALEXEY\DESKTOP\CCC\UNIT1.OBJ
Upvotes: 0
Views: 975
Reputation: 595329
You have declared a class T1
that has a constructor declared in it, but you did not actually implement that constructor. That is what the linker is complaining about.
You need to add that implementation to your .cpp
file, eg:
__fastcall T1::T1(bool CreateSuspended)
: TThread(CreateSuspended)
{
FreeOnTerminate = true; // <-- this should be moved in here
}
That will solve the linker error.
That being said, you have some design flaws in your code.
You are creating the Thread1
object with CreateSuspended=false
, which gives it permission to start running immediately once its constructor is finished. But you are not assigning its Timer
member until after the constructor has exited. So the thread can start running before its Timer
is assigned. The only reason this "works" in your example is because your use of TThread::Synchronize()
will not call T1::UpdateTimer()
until after TForm1::FormCreate()
1 has assigned the Timer
member and exited
1: BTW, you should NOT be using that event in C++ at all, as it can cause undefined behavior. Use the Form's constructor instead.
But this is not a good design to rely on. Since your thread class depends on an external TTimer
object, you should either:
create the Thread1
object with CreateSuspended=true
instead, and then Resume()
that thread after you have fully initialized its members. Which is what you are doing with the Thread2
object.
pass the desired TTimer
object pointer to each thread's constructor, eg:
class T1 : public TThread
{
private:
TTimer *Timer;
void _fastcall UpdateTimer();
protected:
void _fastcall Execute();
public:
__fastcall T1(bool CreateSuspended, TTimer *ATimer);
};
...
__fastcall T1::T1(bool CreateSuspended, TTimer *ATimer)
: TThread(CreateSuspended), Timer(ATimer)
{
FreeOnTerminate = true;
}
...
Thread1 = new T1(false, Timer1);
Thread2 = new T1(true, Timer2);
Also, you are setting FreeOnTerminate=true
for both threads, but you are not assigning an OnTerminate
event handler to each thread, so you have no way to know for sure if the threads are actually valid when you try to access them in your button OnClick
event handlers. If the threads terminate for any reason, including an unexpected crash, then you will be accessing dangling pointers.
Also, you should not be Suspend()
'ing a running thread from outside of that thread's own Execute()
method for ANY reason. You don't know the state of the thread at the time of the suspension. It could be (and likely will be, in this case) in the middle of a Synchronize()
call, which can cause deadlocks that are waiting on the suspended Synchronize()
to finish its work. If you want to pause a worker thread, use a separate flag instead, or even a TEvent
, which the thread can check periodically when it is safe and appropriate to do so.
Lastly, calling TThread::Synchronize()
in a tight loop in a worker thread, as you are doing, is a complete waste of a worker thread, since ALL of its work is being performed in the main UI thread only. You may as well just get rid of your TThread
class completely. Using a TTimer
instead will suffice, or a PostMessage()
loop, or a TApplication::OnIdle
event handler, or a TAction(Manager)::OnUpdate
event handler, etc. Many different ways to approach this.
Upvotes: 1