I still ask about thread synchronization

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

I still ask about thread synchronization

Postby mark_c » Tue Jun 11, 2019 4:42 am

Hello,
an old problem has already been discussed here, but I was observing that there is no mechanism to synchronize the VCL and CreateThread () methods. If from CreateThread I try to write in a label I almost certainly have a collision.
Is there a way?
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby rlebeau » Tue Jun 11, 2019 12:48 pm

mark_c wrote:I was observing that there is no mechanism to synchronize the VCL and CreateThread () methods.


Sure, there is - the TThread::Synchronize() and TThread::Queue() methods, which have static versions available that can be called outside of TThread-oriented threads.

Or, you can use the Win32 SendMessage() and PostMessage() functions to send messages to UI windows, and then have the message handlers do the UI work as needed.

mark_c wrote:If from CreateThread I try to write in a label I almost certainly have a collision.
Is there a way?


You must sync with the main UI thread in order to access VCL controls correctly and safely.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1591
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: I still ask about thread synchronization

Postby mark_c » Tue Jun 11, 2019 1:06 pm

thanks Remy.
Do you have any examples of use of CreateThread () that writes in a Label (UI) managed by the VCL?

Thank you
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby rlebeau » Tue Jun 11, 2019 2:57 pm

mark_c wrote:Do you have any examples of use of CreateThread () that writes in a Label (UI) managed by the VCL?


There are many different ways to handle this:

1. using the static TThread methods:

Code: Select all
void __fastcall TForm1::DoUpdateLabel1()
{
    String S = String().sprintf(_D("Hello @ %u"), GetTickCount());
    Label1->Caption = S;
}

// Thread procedure used with CreateThread()...
DWORD WINAPI MyThreadProc(LPVOID lpParameter)
{
    TThread::Synchronize(NULL, Form1->DoUpdateLabel1);
    or:
    TThread::Queue(NULL, Form1->DoUpdateLabel1);

    return 0;
}


Alternatively:

Code: Select all
struct Label1Updater
{
    String NewCaption;

    void __fastcall DoUpdateSync()
    {
        Form1->Label1->Caption = NewCaption;
    }

    void __fastcall DoUpdateAsync()
    {
        DoUpdateSync();
        delete this;
    }
};

void __fastcall DoUpdateLabel1(String NewCaption, bool Synchronous)
{
    if (Synchronous)
    {
        Label1Updater updater;
        updater.NewCaption = NewCaption;
        TThread::Synchronize(NULL, updater.DoUpdateSync);
    }
    else
    {
        Label1Updater *updater = new Label1Updater;
        updater->NewCaption = NewCaption;
        TThread::Queue(NULL, updater->DoUpdateAsync);
    }
}

DWORD WINAPI MyThreadProc(LPVOID lpParameter)
{
    String S = String().sprintf(_D("Hello @ %u"), GetTickCount());

    DoUpdateLabel1(S, true);
    or:
    DoUpdateLabel1(S, false);

    return 0;
}


Alternatively (Clang-based compilers only, see How to Handle Delphi Anonymous Methods in C++: Using a Lambda Expression):

Code: Select all
DWORD WINAPI MyThreadProc(LPVOID lpParameter)
{
    String S = String().sprintf(_D("Hello @ %u"), GetTickCount());

    TThread::Synchronize(NULL,
        [=](){ Form1->Label1->Caption = S; }
    );

    or:

    TThread::Queue(NULL,
        [=](){ Form1->Label1->Caption = S; }
    );

    return 0;
}


Alternatively (non CLang-based compilers only, see How to Handle Delphi Anonymous Methods in C++: Using a Functor (Function Object)):

Code: Select all
struct Label1Updater
{
    String NewCaption;

    Label1Updater(String s) : NewCaption(s) {}
    bool operator()() { Form1->Label1->Caption = NewCaption; }
};
 
DWORD WINAPI MyThreadProc(LPVOID lpParameter)
{
    String S = String().sprintf(_D("Hello @ %u"), GetTickCount());

    typedef TMethodRef<TThreadProcedure, Label1Updater, void> MyMethRef;
    _di_TThreadProcedure proc(new MyMethRef(Label1Updater(S)));

    TThread::Synchronize(NULL,  proc);
    or:
    TThread::Queue(NULL, proc);

    return 0;
}


2. using the Win32 API:

Code: Select all
const UINT APPWM_UPDATELABEL1 = WM_APP + 1;

__fastcall TForm1::TForm1(TComponent *Owner)
    : TForm(Owner)
{
    FMyThreadWnd = AllocateHWnd(MyThreadWndProc);
}

__fastcall TForm1::~TForm1()
{
    DeallocateHWnd(FMyThreadWnd);
};

void __fastcall TForm1::MyThreadWndProc(TMessage &Message)
{
    if (Message.Msg == APPWM_UPDATELABEL1)
    {
        String *S = reinterpret_cast<String*>(Message.LParam);
        Label1->Caption = *S;
        if (Message.WParam)
            delete S;
    }
    else
        Message.Result = DefWindowProc(FMyThreadWnd, Message.Msg, Message.WParam, Message.LParam);
}

void __fastcall TForm1::UpdateLabel1(String NewCaption, bool Synchronous)
{
    if (Synchronous)
    {
        SendMessage(FMyThreadWnd, APPWM_UPDATELABEL1, 0, reinterpret_cast<LPARAM>(&S));
    }
    else
    {
        String *S = new String(NewCaption);
        if (!PostMessage(FMyThreadWnd, APPWM_UPDATELABEL1, 1, reinterpret_cast<LPARAM>(S)))
            delete S;
    }
}

DWORD WINAPI MyThreadProc(LPVOID lpParameter)
{
    String S = String().sprintf(_D("Hello @ %u"), GetTickCount());

    Form1->UpdateLabel1(S, true);
    or:
    Form1->UpdateLabel1(S, false);

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

Re: I still ask about thread synchronization

Postby mark_c » Wed Jun 12, 2019 7:04 am

thanks as always Remy,
I believe the time has come to retire the good BCB6, companion of many adventures.
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby HsiaLin » Wed Jun 12, 2019 1:10 pm

I had many doubts about leaving bcb6 too but once you get used to the new IDE its much better than the old ways, in my opinion.
HsiaLin
BCBJ Master
BCBJ Master
 
Posts: 314
Joined: Sun Jul 08, 2007 6:29 pm

Re: I still ask about thread synchronization

Postby mark_c » Wed Jun 12, 2019 1:30 pm

I didn't want to change the glorious BCB6 because the BCB6 projects are not directly compatible with Embarcadero but I have to copy and paste an infinite number of times.
Anyway thanks for your testimony.
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby HsiaLin » Wed Jun 12, 2019 7:11 pm

You dont have to copy and paste, just make a new empty project and add your old cpp and h files to it. Dont forget to add the .dfm files to the folder too.

You`ll probably run into a couple things that need to change, like any Strings you have declared will be unicode strings in XE.
HsiaLin
BCBJ Master
BCBJ Master
 
Posts: 314
Joined: Sun Jul 08, 2007 6:29 pm

Re: I still ask about thread synchronization

Postby mark_c » Thu Jun 13, 2019 12:55 am

thank you for the tip; but must the UI be redesigned or is there some trick?
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby mark_c » Thu Jun 13, 2019 12:58 am

I ask you if you think there may be collisions in this version, I don't see any

thank you

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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

bool bAbort;

void SendMyMessage(char *msg);

DWORD WINAPI Thread1( LPVOID lpParameter );
DWORD WINAPI Thread2( LPVOID lpParameter );
DWORD ID1, ID2;
HANDLE Trd1, Trd2;




int a, b;
AnsiString msg;

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
   ServerSocket1->Port = 5000;
   ServerSocket1->Active = True;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   bAbort = false;

   Trd1 = CreateThread(NULL,0,Thread1,NULL,0,&ID1);
   Trd2 = CreateThread(NULL,0,Thread2,NULL,0,&ID2);

   Button1->Enabled=false;
   Button2->Enabled=true;

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

void __fastcall TForm1::Button2Click(TObject *Sender)
{
   bAbort = true;

   Button1->Enabled=true;
   Button2->Enabled=false;

   Form1->Caption = "Stopped.....";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::DoUpdateLabel()
{
   Label1->Caption = a;
   Label2->Caption = b;
}
//---------------------------------------------------------------------------
DWORD WINAPI Thread1( LPVOID lpParameter )
{
   while(!bAbort)
   {
      msg=" msg1 ";
      TThread::Synchronize(NULL, Form1->SendMyMessage);
      a++;
      TThread::Synchronize(NULL, Form1->DoUpdateLabel);
      Sleep(1);
   }
   return 0;
}
//---------------------------------------------------------------------------

DWORD WINAPI Thread2( LPVOID lpParameter )
{
   while(!bAbort)
   {
      msg=" msg2 ";
      TThread::Synchronize(NULL, Form1->SendMyMessage);
      b++;
      TThread::Synchronize(NULL, Form1->DoUpdateLabel);
      Sleep(1);
   }
   return 0;
}

//---------------------------------------------------------------------------

//void SendMyMessage(char *msg)
void __fastcall TForm1::SendMyMessage()
{
   try{
      for(int actconn = 0; actconn < Form1->ServerSocket1->Socket->ActiveConnections; actconn++)
      Form1->ServerSocket1->Socket->Connections[actconn]->SendText(msg);

   } catch(...) { }

}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
   TerminateThread(Trd1,0);
   TerminateThread(Trd2,0);

   ServerSocket1->Active = False;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ServerSocket1ClientError(TObject *Sender, TCustomWinSocket *Socket,
          TErrorEvent ErrorEvent, int &ErrorCode)
{
   if (ErrorCode == 10053) {
      Socket->Close();
   }

   ErrorCode = 0;
}
//---------------------------------------------------------------------------
mark_c
BCBJ Guru
BCBJ Guru
 
Posts: 160
Joined: Thu Jun 21, 2012 1:13 am

Re: I still ask about thread synchronization

Postby HsiaLin » Thu Jun 13, 2019 10:10 am

mark_c wrote:thank you for the tip; but must the UI be redesigned or is there some trick?


Your programs UI is stored in the .dfm files. As long as you have cpp, h and dfm files together you good to go.
HsiaLin
BCBJ Master
BCBJ Master
 
Posts: 314
Joined: Sun Jul 08, 2007 6:29 pm


Return to Technical

Who is online

Users browsing this forum: No registered users and 16 guests

cron