How to terminate a thread correctly

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Re: How to terminate a thread correctly

Postby mark_c » Wed Apr 01, 2015 6:51 am

take this opportunity again to a further question: I used your source code that I have adapted to my earlier example.
Now I ask you if this source code could have problems race condition?

thank you

Code: Select all
//---------------------------------------------------------------------------

#include <vcl\vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <stdio.h>


class TMyThread : public TThread
{
protected:
virtual void __fastcall Execute();
public:
__fastcall TMyThread();
void __fastcall TMyThread::DisplayLabel();
};

TMyThread *MyThread = NULL;
int i;

//---------------------------------------------------------------------------
#pragma link "Grids"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

__fastcall TMyThread::TMyThread()
        : TThread(false)
{
}
//---------------------------------------------------------------------------

void __fastcall TMyThread::DisplayLabel()
{
  Form1->Label1->Caption = i;
}
//---------------------------------------------------------------------------

void __fastcall TMyThread::Execute()
{
        while (!Terminated)
        {
                for(i = 0; i < 625000; ++i)
                {
                        if (Terminated) return;
                        Synchronize(DisplayLabel);
                }
                if(Terminated) return;
        }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
        // if thread is already running destroy it
        if (MyThread)
        {
            MyThread->Terminate();
            MyThread->WaitFor();
            delete MyThread;
            MyThread = NULL;
        }

        Caption = "Stopped.....";
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
        Button2->Click();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
        // stop thread if already start
        if(MyThread)
                Button2->Click();

        // then create a new thread
        MyThread = new TMyThread;

        Caption = "Started.....";
}
//---------------------------------------------------------------------------
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby mark_c » Thu Apr 02, 2015 12:48 pm

alternative to the previous code I tried this solution is correct?

I also wonder how much is lost in performance using TcriticalSection

thank you


Code: Select all
void __fastcall TMyThread::Execute()
{
        TCriticalSection *mm;;

        mm = new  TCriticalSection;

        while (!Terminated)
        {
                for(i = 0; i < 625000; ++i)
                {
                        if (Terminated) return;

                        mm->Acquire();
                        try{
                        Form1->Label1->Caption = i;
                        Sleep(5);
                        //Synchronize(DisplayLabel);
                        }
                        __finally
                        {
                        mm->Release();
                        }
                }
                if(Terminated) return;
        }
}
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby rlebeau » Thu Apr 02, 2015 1:14 pm

mark_c wrote:alternative to the previous code I tried this solution is correct?


No, you are NOT using the critical section correctly. Only your worker thread ever touches the CS, your main UI thread never even sees it. The UI thread and the worker thread must both obtain access to the same CS lock in order to safely access anything that is shared amongst the threads. However, UI controls are accessed by not just you, but also by the VCL internally, and even Windows itself. And because of that, you CANNOT use a CS (or a mutex, or a semaphore, or any other such object) to synchronize UI access. You have to use a different solution.

You already know how to use TThread::Synchronize() and SendMessage() for synchronizing. There are many options available. For example, using the Interlocked API to safely update the value of a shared variable:

Code: Select all
LONG value = 0;

void __fastcall TForm1::Timer1Timer()
{
    Label1->Caption = InterlockedIncrement(&value, 0);
}

void __fastcall TMyThread::Execute()
{
    while (!Terminated)
    {
        for(LONG i = 0; (i < 625000) && (!Terminated); ++i)
        {
            InterlockedExchange(&value, i);
            Sleep(5);
        }
    }
}


Or using a critical section/mutex/semaphore to update a shared variable, rather than access the UI directly:

Code: Select all
TCriticalSection *cs = NULL;
String value;

__fastcall TForm1::TForm1(TComponent *Owner)
    : TForm(Owner)
{
    cs = new TCriticalSection;
}

__fastcall TForm1::~TForm1()
{
    delete cs;
}

void __fastcall TForm1::Timer1Timer()
{
    cs->Enter();
    Label1->Caption = value;
    cs->Leave();
}

void __fastcall TMyThread::Execute()
{
    while (!Terminated)
    {
        for(int i = 0; (i < 625000) && (!Terminated); ++i)
        {
            cs->Enter();
            value = i;
            cs->Leave();
            Sleep(5);
        }
    }
}


mark_c wrote:I also wonder how much is lost in performance using TcriticalSection


There are plenty of studies available online that discuss critical section performance. If you want something more efficient, look at TMultiReadExclusiveWriteSynchronizer or Slim Read/Write Locks.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1533
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: How to terminate a thread correctly

Postby mark_c » Fri Apr 03, 2015 2:01 am

thanks again; I wonder, however, in this case the threads are not synchronized correct?
I saw, in fact, that the values shown in the Label are not continuous but almost random, correct?

Code: Select all
TCriticalSection *cs = NULL;
String value;

__fastcall TForm1::TForm1(TComponent *Owner)
    : TForm(Owner)
{
    cs = new TCriticalSection;
}

__fastcall TForm1::~TForm1()
{
    delete cs;
}

void __fastcall TForm1::Timer1Timer()
{
    cs->Enter();
    Label1->Caption = value;
    cs->Leave();
}

void __fastcall TMyThread::Execute()
{
    while (!Terminated)
    {
        for(int i = 0; (i < 625000) && (!Terminated); ++i)
        {
            cs->Enter();
            value = i;
            cs->Leave();
            Sleep(5);
        }
    }
}
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby rlebeau » Fri Apr 03, 2015 1:45 pm

mark_c wrote:I wonder, however, in this case the threads are not synchronized correct?


Yes, they are. They are sharing a single critical section object. When one thread has the lock, the other thread has to wait until the lock is released.

mark_c wrote:I saw, in fact, that the values shown in the Label are not continuous but almost random, correct?


Because the example is using a timer in the UI thread, and the timer runs slower than the speed of the worker thread loop, so the UI thread is not going to see every individual value that is saved by the worker thread, only the latest value at the moment the timer is triggered.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1533
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: How to terminate a thread correctly

Postby mark_c » Sat Apr 04, 2015 4:02 am

what are the major differences when using Syncronize in place of TCriticalSection ?

thank you
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby rlebeau » Sun Apr 05, 2015 4:10 pm

mark_c wrote:what are the major differences when using Syncronize in place of TCriticalSection ?


They are fundamentally different mechanisms.

TCriticalSection is just a lock, nothing more. While one thread has ownership of the lock, no other thread can obtain the lock until the owning thread releases it. Code inside the lock runs in the context of the thread that has the lock.

Synchronize() puts an object method pointer into a queue and waits for the main UI thread to execute it. Code inside synched methods runs in the context of the main thread.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1533
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: How to terminate a thread correctly

Postby mark_c » Tue Apr 07, 2015 1:37 am

hello, however, I have another question: if in a form I Label 10, I will have to write 10 functions similar to:

Code: Select all
void __fastcall TMyThread::DisplayLabel1() {
}

void __fastcall TMyThread::DisplayLabel2() {
}

void __fastcall TMyThread::DisplayLabel10() {
}


Synchronize(DisplayLabel1);
Synchronize(DisplayLabel2);
......




thank you

Code: Select all
    //---------------------------------------------------------------------------

    #include <vcl\vcl.h>
    #pragma hdrstop

    #include "Unit1.h"
    #include <stdio.h>


    class TMyThread : public TThread
    {
    protected:
    virtual void __fastcall Execute();
    public:
    __fastcall TMyThread();
    void __fastcall TMyThread::DisplayLabel();
    };

    TMyThread *MyThread = NULL;
    int i;

    //---------------------------------------------------------------------------
    #pragma link "Grids"
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------

    __fastcall TMyThread::TMyThread()
            : TThread(false)
    {
    }
    //---------------------------------------------------------------------------

    void __fastcall TMyThread::DisplayLabel()
    {
      Form1->Label1->Caption = i;
    }
    //---------------------------------------------------------------------------

    void __fastcall TMyThread::Execute()
    {
            while (!Terminated)
            {
                    for(i = 0; i < 625000; ++i)
                    {
                            if (Terminated) return;
                            Synchronize(DisplayLabel);
                    }
                    if(Terminated) return;
            }
    }
    //---------------------------------------------------------------------------

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
            // if thread is already running destroy it
            if (MyThread)
            {
                MyThread->Terminate();
                MyThread->WaitFor();
                delete MyThread;
                MyThread = NULL;
            }

            Caption = "Stopped.....";
    }
    //---------------------------------------------------------------------------

    void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
    {
            Button2->Click();
    }
    //---------------------------------------------------------------------------

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
            // stop thread if already start
            if(MyThread)
                    Button2->Click();

            // then create a new thread
            MyThread = new TMyThread;

            Caption = "Started.....";
    }
    //---------------------------------------------------------------------------






and again, sorry .......

if a user-defined function example:

Code: Select all
void __fastcall TMyThread :: Execute ()
     {
             while (! Terminated)
             {
                     myfunction (100);
                     if (Terminated) return;
             }
     }

int myfunction (int a)
{
      Form1->Label1->Caption = a;
      return 0;
}


called by the secondary thread needs to change a Label in security, how can do it?
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby rlebeau » Tue Apr 07, 2015 12:55 pm

mark_c wrote:hello, however, I have another question


I feel like we are going around in circles. This should not be a complicated concept to understand.

mark_c wrote:if in a form I Label 10, I will have to write 10 functions similar to


If you want to update all 10 labels at the same time, you only need 1 synched function:

Code: Select all
void __fastcall TMyThread::DisplayAllLabels()
{
    // update all 10 Labels here
}


Code: Select all
Synchronize(DisplayAllLabels);


If you want the ability to both individual Labels at separate times, while also updating all labels together, you can do this:

Code: Select all
void __fastcall TMyThread::DisplayLabel1()
{
    // update Label1 here
}

void __fastcall TMyThread::DisplayLabel2()
{
    // update Label2 here
}

...

void __fastcall TMyThread::DisplayAllLabels()
{
    DisplayLabel1();
    DisplayLabel2();
    // ...
}


Code: Select all
Synchronize(DisplayLabel1);
...
Synchronize(DisplayAllLabels);


mark_c wrote:if a user-defined function example:

Code: Select all
void __fastcall TMyThread :: Execute ()
{
    while (! Terminated)
    {
        myfunction (100);
        if (Terminated) return;
    }
}

int myfunction (int a)
{
    Form1->Label1->Caption = a;
    return 0;
}


called by the secondary thread needs to change a Label in security, how can do it?


The exact same way that the previous Execute() examples do it. myfunction() will have to save its desired value somewhere safe and then Synchronize() the label update, eg:

Code: Select all
class TMyThread : public TThread
{
private
    int Label1Value;
    void __fastcall DisplayLabel1();
    int myfunction (int a);
protected:
    void __fastcall Execute();
};

void __fastcall TMyThread::Execute()
{
    while (!Terminated)
    {
        myfunction(100);
    }
}

void __fastcall TMyThread::DisplayLabel1()
{
    Form1->Label1->Caption = Label1Value;
}

int TMyThread::myfunction(int a)
{
    Label1Value = a;
    Synchronize(DisplayLabel1);
    return 0;
}


If myfunction() is not a member of the thread class, you can use a helper class:

Code: Select all
class TUpdateLabel1
{
public:
    int Value;
    void __fastcall Display();
};

void __fastcall TUpdateLabel1::Display()
{
    Form1->Label1->Caption = Value;
}

int myfunction(int a)
{
    TUpdateLabel1 update;
    update.Value = a;
    TThread::Synchronize(NULL, update.Display);
    return 0;
}
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1533
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: How to terminate a thread correctly

Postby mark_c » Tue Apr 07, 2015 1:16 pm

thanks again, but I'm starting to see the output from the circle. :)
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby mark_c » Wed Apr 08, 2015 12:48 am

If you want to update all 10 labels at the same time, you only need 1 synched function:


sorry, but I think I explained bad: if I have this situation I have to keep duplicate code like the one shown below, or there are more refined ways?

thank you

Code: Select all

    class TMyThread : public TThread
    {
    private
        int Label1Value1, Label1Value2, Label1Value3, Label1Value4, Label1Value5;
        void __fastcall DisplayLabel1();
        void __fastcall DisplayLabel2();
        void __fastcall DisplayLabel3();
        void __fastcall DisplayLabel4();
        void __fastcall DisplayLabel5();

        int myfunction1(int a);
        int myfunction2(int a);
        int myfunction3(int a);
        int myfunction4(int a);
        int myfunction5(int a);
    protected:
        void __fastcall Execute();
    };
//---------------------------------------------------------------------------
    void __fastcall TMyThread::Execute()
    {
        while (!Terminated)
        {
            myfunction1(100);
            myfunction2(200);
            myfunction3(300);
            myfunction4(400);
            myfunction5(500);
        }
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel1()
    {
        Form1->Label1->Caption = Label1Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel2()
    {
        Form1->Label2->Caption = Label2Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel3()
    {
        Form1->Label3->Caption = Label3Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel4()
    {
        Form1->Label4->Caption = Label4Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel5()
    {
        Form1->Label5->Caption = Label5Value;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction1(int a)
    {
        Label1Value = a;
        Synchronize(DisplayLabel1);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction2(int a)
    {
        Label2Value = a;
        Synchronize(DisplayLabel2);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction3(int a)
    {
        Label3Value = a;
        Synchronize(DisplayLabel2);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction4(int a)
    {
        Label4Value = a;
        Synchronize(DisplayLabel3);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction5(int a)
    {
        Label5Value = a;
        Synchronize(DisplayLabel4);
        return 0;
    }
//---------------------------------------------------------------------------

mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Re: How to terminate a thread correctly

Postby rlebeau » Wed Apr 08, 2015 1:22 am

mark_c wrote:sorry, but I think I explained bad: if I have this situation I have to keep duplicate code like the one shown below, or there are more refined ways?


Gather the values first, then Synchronize() them all at one time

Code: Select all
    class TMyThread : public TThread
    {
    private
        int Label1Value1, Label1Value2, Label1Value3, Label1Value4, Label1Value5;
        void __fastcall DisplayLabels();
        void __fastcall DisplayLabel1();
        void __fastcall DisplayLabel2();
        void __fastcall DisplayLabel3();
        void __fastcall DisplayLabel4();
        void __fastcall DisplayLabel5();

        int myfunction1(int a);
        int myfunction2(int a);
        int myfunction3(int a);
        int myfunction4(int a);
        int myfunction5(int a);
    protected:
        void __fastcall Execute();
    };
//---------------------------------------------------------------------------
    void __fastcall TMyThread::Execute()
    {
        while (!Terminated)
        {
            myfunction1(100);
            myfunction2(200);
            myfunction3(300);
            myfunction4(400);
            myfunction5(500);
            Synchronize(DisplayLabels);
        }
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel1()
    {
        Form1->Label1->Caption = Label1Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel2()
    {
        Form1->Label2->Caption = Label2Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel3()
    {
        Form1->Label3->Caption = Label3Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel4()
    {
        Form1->Label4->Caption = Label4Value;
    }
//---------------------------------------------------------------------------
    void __fastcall TMyThread::DisplayLabel5()
    {
        Form1->Label5->Caption = Label5Value;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction1(int a)
    {
        Label1Value = a;
        //Synchronize(DisplayLabel1);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction2(int a)
    {
        Label2Value = a;
        //Synchronize(DisplayLabel2);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction3(int a)
    {
        Label3Value = a;
        //Synchronize(DisplayLabel3);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction4(int a)
    {
        Label4Value = a;
        //Synchronize(DisplayLabel4);
        return 0;
    }
//---------------------------------------------------------------------------
    int TMyThread::myfunction5(int a)
    {
        Label5Value = a;
        //Synchronize(DisplayLabel5);
        return 0;
    }
//---------------------------------------------------------------------------

Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1533
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: How to terminate a thread correctly

Postby mark_c » Wed Apr 08, 2015 1:41 am

edit
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 129
Joined: Thu Jun 21, 2012 1:13 am

Previous

Return to Technical

Who is online

Users browsing this forum: Bing [Bot], Majestic-12 [Bot] and 6 guests

cron