Multithread GUI

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Multithread GUI

Mensagem por JoséQuintas »

error2.png
Neste caso é fivewin, mas poderia ser com qualquer uso.

Código: Selecionar todos

#include "fivewin.ch"

PROCEDURE ze_fwTextMsg

   LOCAL oDlgMsg, nCont, bOldError

   hb_ThreadStart( { || oDlgMsg := DlgTextMsgClass():New(), oDlgMsg:Execute() } )

   bOldError := ErrorBlock()
   ErrorBlock( { | e |  oDlgMsg:lCanClose := .T., oDlgMsg:End(), iif( bOldError == Nil, Nil, Eval( bOldError, e ) ) } )

   Inkey(2)
   FOR nCont = 1 TO 50
      oDlgMsg:ShowText( "Test " + Ltrim( Str( nCont ) ) )
      oDlgMsg:nGAtual += 2
      oDlgMsg:ShowMeter( oDlgMsg:nGAtual )
      Inkey(0.5)
   NEXT
   ? oDlgMsg / 3 // force error
   oDlgMsg:lCanClose := .T.
   oDlgMsg:End()

   RETURN

CREATE CLASS DlgTextMsgClass

   VAR xDlg
   VAR oBrowse
   VAR oGet
   VAR oSay1
   VAR oSay2
   VAR oMeter
   VAR aText      INIT { "." }
   VAR cText
   VAR cText1     INIT ""
   VAR cText2     INIT ""
   VAR nMaxRow    INIT 10
   VAR nStyle     INIT 2
   VAR lCanClose  INIT .F.
   VAR lGraph     INIT .T.
   VAR nGAtual    INIT 0
   VAR nGTotal    INIT 100
   VAR nGSecStart INIT 0
   VAR nGSecNow   INIT 0

   METHOD Execute()
   METHOD ShowText( cText )
   METHOD ShowMeter( nValue )
   METHOD End() INLINE ::xDlg:End()

   ENDCLASS

METHOD Execute() CLASS DlgTextMsgClass

   LOCAL nDlgWidth, nDlgHeight // xStatusbar,
   LOCAL xControl, oCol

   ::nGSecStart := Int( Seconds() )
   nDlgWidth  := AppWindowInfo()[1]
   nDlgHeight := AppWindowInfo()[2]
   ::nMaxRow  := Int( nDlgHeight / ( AppFontSize() + 2 ) - 2 )

   DEFINE DIALOG ::xDlg FROM 0, 0 TO nDlgHeight, nDlgWidth PIXEL ;
      FONT AppFont()  ;
      TITLE "TextScroll" // COLOR COLOR_WHITE, CLR_JPA
   ::xDlg:SetIcon( TIcon():New(,,"APPICON" ) )
   ::xDlg:bValid := { || ::lCanClose }

   IF ::lGraph

   @ nDlgHeight - 80, 40 SAY ::oSay1 VAR ::cText1 OF ::xDlg PIXEL ;
      SIZE 500, AppFontSize() FONT AppFont() COLOR CLR_BLACK, CLR_WHITE // TRANSPARENT BORDER

   @ nDlgHeight - 80, nDlgWidth - 550 SAY ::oSay2 VAR ::cText2 OF ::xDlg PIXEL ;
      SIZE 500, AppFontSize() FONT AppFont() RIGHT COLOR CLR_BLACK, CLR_WHITE // TRANSPARENT BORDER

      @ nDlgHeight - 60, 40 METER ::oMeter VAR ::nGAtual ;
         SIZE nDlgWidth - 80, AppFontSize() * 2 PIXEL OF ::xDlg ;
         COLOR    CLR_HGRAY, CLR_BLACK ;
         BARCOLOR CLR_MAGENTA, CLR_WHITE ;
         TOTAL ::nGTotal FONT AppFont() BORDER CLR_BLACK UPDATE

   ENDIF
   IF ::nStyle == 1
      @ 10, 10 GET ::oGet VAR ::cText MEMO OF ::xDlg PIXEL ;
         SIZE nDlgWidth  - AppFontSize(), nDlgHeight - AppFontSize() - iif( ::lGraph, 100, 0 ) ;
         FONT AppFont() COLOR CLR_BLACK, CLR_WHITE // TRANSPARENT BORDER
   ENDIF
   //::xGet:Disable()
   //Don't works for GET MEMO
   //::xGet:lDisColors := .F.
   //::xGet:nClrTextDis := RGB2N(20,20,20)
   IF ::nStyle == 2
      @ 10, 10 XBROWSE xControl ;
         ARRAY { "" } ;
         SIZE nDlgWidth  - AppFontSize(), nDlgHeight - AppFontSize() - iif( ::lGraph, 100, 0 ) ;
          FONT AppFont() PIXEL
      oCol := xControl:AddCol()
      oCol:cHeader := "Text"
      oCol:bStrData := { || xControl:aArrayData[ xControl:nArrayAt ] }
      xControl:lFitGridHeight := .T. // adjust extra space to header/footer
      xControl:nStretchCol := STRETCHCOL_WIDEST
      xControl:CreateFromCode()
      ::oBrowse := xControl
   ENDIF

   ACTIVATE DIALOG ::xDlg CENTERED
      //ON INIT ( (Self), guistatusBarCreate( ::xDlg, @xStatusbar, "" ) )

   RETURN Nil

METHOD ShowText( cText ) CLASS DlgTextMsgClass

   LOCAL cItem

      IF Len( ::aText ) = ::nMaxRow
         ADel( ::aText, 1 )
         ::aText[ Len( ::aText ) ] := cText
      ELSE
         AAdd( ::aText, cText )
      ENDIF
      ::cText := ""
      FOR EACH cItem IN ::aText
         ::cText += cItem + hb_Eol()
      NEXT
   IF ::nStyle == 1
      ::oSay:VarPut( ::cText )
      ::oSay:Refresh()
   ENDIF
   IF ::nStyle == 2
      ::oBrowse:SetArray( ::aText )
      ::oBrowse:nArrayAt := Len( ::aText )
      ::oBrowse:Refresh()
   ENDIF

   RETURN Nil

METHOD ShowMeter( nValue ) CLASS DlgTextMsgClass

   LOCAL nSecElapsed, nSecRemaining

   IF ::nGSecNow != Int( Seconds() )
      ::nGSecNow := Seconds()
      ::nGAtual  := nValue

      nSecElapsed := ::nGSecNow - ::nGSecStart
      DO WHILE nSecElapsed < 0
         nSecElapsed += ( 24 * 3600 )
      ENDDO
      nSecRemaining := nSecElapsed / ::nGAtual * ( ::nGTotal - ::nGAtual )

      ::oSay1:VarPut( "Proc." + Ltrim( Str( ::nGAtual, 15, 0 ) ) + "/" + ;
         Ltrim( Str( ::nGTotal, 15, 0 ) ) + ;
         " Gasto " + SecText( nSecElapsed ) )
      ::oSay1:Refresh()
      ::oSay2:VarPut( "Falta " + SecText( nSecRemaining ) )
      ::oSay2:Refresh()
      ::xDlg:Update()
   ENDIF

   RETURN Nil

STATIC FUNCTION SecText( nS )

   LOCAL nH, nM

   nM := Int( nS / 60 )
   nS -= ( nM * 60 )
   nH := Int( nM / 60 )
   nM -= ( nH * 60 )

   RETURN ;
      Ltrim( Str( nH, 2 ) ) + "h " + ;
      Ltrim( Str( nM, 2 ) ) + "m " + ;
      Ltrim( Str( nS, 2 ) ) + "s"


//STATIC FUNCTION guiStatusBarCreate( xDlg, xStatusbar, cText )

   //DEFINE STATUSBAR xStatusBar PROMPT cText OF xDlg
   //xDlg:Refresh()

   //RETURN xStatusBar
Coloquei um erro proposital

Código: Selecionar todos

   ? oDlgMsg / 3 // force error
pra testar o novo codeblock

Código: Selecionar todos

   bOldError := ErrorBlock()
   ErrorBlock( { | e |  oDlgMsg:lCanClose := .T., oDlgMsg:End(), iif( bOldError == Nil, Nil, Eval( bOldError, e ) ) } )
É meio doido, ao mesmo tempo que não é.

Lembram: multithread é igual EXEs separados.

Tem o "EXE" do módulo, e o "EXE" da dialog.
Se dá erro no módulo.... a dialog fica aberta, precisa fechar no gerenciador de tarefas.
Então, criei um errorblock que fecha a dialog.

Um recurso interessante no fivewin é o bloco VALID no fechamento da dialog.
Se retornar .F., a dialog não fecha, tanto faz se é MODAL ou não.
É pra isso que uso o lCanClose.
Até Dlg:End() fica impedido de fechar, por isso primeiro altero lCanClose := .T. pra depois usar oDlg:End()

Fico imaginando que as LIBs GUI poderiam usar errorblocks desse jeito, pra facilitar trabalho do programador.
Não me perguntem aonde, cada situação precisa solução diferente.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Multithread GUI

Mensagem por JoséQuintas »

Esse tipo de coisa serve pra QUALQUER LIB GUI, com multithread, e com ou sem mistura de LIB.

É novidade pra mim usar numa situação dessas, e achei interessante.

Por enquanto são testes, só pra ver possibilidades.

Nesse caso é usar uma rotina pra ficar mostrando mensagens, além de poder mostrar um gráfico de tempo.
A partir da GTWVG, chamar essa rotina que faz uso de uma dialog FIVEWIN.
Mas poderia ser HWGUI, MINIGUI, chamando elas mesmas ou outra LIB em multithread.

Meu tempo de uso de GTWVG com multithread foi muito bom.
As coisas ficam mais visíveis com ela.
Com isso, dá pra entender o que acontece nas LIBs GUI, mesmo quando não dá pra ver.

Pras LIBs GUI isso seria interessante até sem multithread.
Mas hoje elas partem pro normal: fechar todo aplicativo, ou simplesmente esconder o erro, o que dá muito trabalho pro programador.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Multithread GUI

Mensagem por JoséQuintas »

São coisas simples.
O funcionamento em conjunto é que torna o negócio interessante.




https://www.youtube.com/watch?v=3vc8u7zd8MQ

Nota:
Só pra lembrar.
Teste direto no aplicativo.
Significa que vai pra cliente, apenas não vai ter na senha deles.
Deixando de ser teste, aí sim vai aparecer, como já fiz com o browse.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
developer
Usuário Nível 3
Usuário Nível 3
Mensagens: 149
Registrado em: 09 Nov 2024 23:45
Localização: Londrina/PR

Multithread GUI

Mensagem por developer »

Eu acho muito interessante usar multithread, só precisei usar uma vez até hoje, para rodar dois processos simultaneamente, funciona muito bem.
Aproveitei que o meu PC estava muito lento, bastava abrir o Chrome e o processamento ia a 100%, problema crônico de 1 core, mas também era um Pentium G2020 2.9GHz.
Comprei um processador compatível com a placa, um Core i5-3570 de 3.4GHz (3.8GHz no turbo) de 4 núcleos, é de 3ª geração, mas está louco de bom, assim além de resolver o problema de lentidão e poderei testar melhor o MT.

Parece que MT aproveita melhor processadores de vários núcleos, segundo encontrei na documentação, mas não tenho confirmação, alguém testou? Viu a diferença?
In Harbour MT mode is fully functional and AFAIK is production ready
without any known bugs except 4 document problems which has not been
addressed and maybe never will be because they are not strictly
necessary to create final applications:
1. Tracelog is not MT safe. It's not a big problem for normal
applications because this code is disabled in standard builds and
enabled only on explicit user request during Harbour compilation
to debug some Harbour internals so it's rather for developers only.
2. PROFILLER cannot collect information for each thread separately.
It's optional code disabled by default. Before we will touch it
please think how it should work for MT programs.
3. DEBUGGER: only main thread debugger can see the names of file wide
STATICs and have information about line numbers with good break
points. We should add code to share this information between threads.
4. hb_threadQuitRequest() can be ignored by thread in some cases
due to race condition. It may happen if thread will overwrite
request send by caller simultaneously, f.e. by its own BREAK.
I can resolve it but we can also leave it as is and document
such behavior as expected or even remove this function. Killing
other threads in such way is dangerous and can be used only
for some simple situation. It's much safer when user uses his
own mechanism to terminate treads in some safe for his code places.


Harbour PRG level API:

hb_threadStart( [<nThreadAttrs> ,] <@sStart()> | <bStart> | <cStart> [, <params,...> ] ) -> <pThID>
hb_threadSelf() -> <pThID> | NIL
hb_threadId( [ <pThID> ] ) -> <nThNo>
hb_threadJoin( <pThID> [, @<xRetCode> ] ) -> <lOK>
hb_threadDetach( <pThID> ) -> <lOK>
* hb_threadQuitRequest( <pThID> ) -> <lOK>
hb_threadTerminateAll() -> NIL
hb_threadWaitForAll() -> NIL
hb_threadWait( <pThID> | <apThID>, [ <nTimeOut> ] [, <lAll> ] ) => <nThInd> | <nThCount> | 0
hb_threadOnce( @<onceControl> [, <bAction> ] ) -> <lFirstCall>
hb_mutexCreate() -> <pMtx>
hb_mutexLock( <pMtx> [, <nTimeOut> ] ) -> <lLocked>
hb_mutexUnlock( <pMtx> ) -> <lOK>
hb_mutexNotify( <pMtx> [, <xVal>] ) -> NIL
hb_mutexNotifyAll( <pMtx> [, <xVal>] ) -> NIL
hb_mutexSubscribe( <pMtx>, [ <nTimeOut> ] [, @<xSubscribed> ] ) -> <lSubscribed>
hb_mutexSubscribeNow( <pMtx>, [ <nTimeOut> ] [, @<xSubscribed> ] ) -> <lSubscribed>

* - this function call can be ignored by the destination thread in some
cases. HVM does not guaranties that the QUIT signal will be always
delivered.

xBase++ compatible functions and classes:
ThreadID() -> <nID>
ThreadObject() -> <oThread>
ThreadWait( <aThreads>, <nTimeOut> ) -> <xResult>
ThreadWaitAll( <aThreads>, <nTimeOut> ) -> <lAllJoind>

Thread() -> <oThread> // create thread object
Signal() -> <oSignal> // create signal object

Harbour supports also SYNC methods and SYNC class method implementing
original xBase++ behavior.

It also supports xBase++ concept of moving workareas between threads
using functions like dbRelease() and dbRequest().

In tests/mt you will find few simple test programs for MT mode.
Some of Harbour tools like hbmk2 or uhttpd[2] (examples/httpsrv,
/examples/uhttpd2) use extensively MT mode.
In MS-Windows only thread which created window can process
window messages so if you are suing GTWVT or GTWVG the main
thread should not be suspended or it will block other threads
which will execute 'inkey(0)' or sth similar. Just simply
incoming keys can be processed only by thread which created
GTWVT window.
It's documented MS-Windows behavior and programmers have to
know about it because it effects all MS-Windows programs in
all languages using any type of interface. Usually the easiest
solution is leaving thread owner to only process window messages
and create other threads to make real job.
In some situations library author can create hidden thread to
process window messages in the background and hide the problem
at least partially for PRG programmers. GTWVT is type of program
where it can be done though one feature blocks it and it will be
necessary to disable it. It's HB_GTI_NOTIFIERBLOCK which cannot
work when messages are processed by non HVM thread.
When GTWIN is used then such hidden thread to process console
window messages is created by system.
Deixo aqui para referência de todos.

Outra referência

E é claro, tem muita informação importante dentro do arquivo xhb-diff.txt que vem com o Harbour.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Multithread GUI

Mensagem por JoséQuintas »

developer escreveu: Usually the easiest
solution is leaving thread owner to only process window messages
and create other threads to make real job.
Interessante essa parte.
Podemos dizer que foi o que eu fiz.
"Deixar uma thread processando mensagens de windows e criar outra(s) thread(s) pra fazer o trabalho real".

Alguns já devem ter visto essa situação, de clicar na janela e o windows dizer que está ocupada não respondendo.
Tem a alternativa DoEvents() ou algo parecido pra contornar isso, mas não dá pra colocar em tudo.
Usando thread, sm tela, não tem que ficar respondendo pro windows.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder