Página 1 de 2

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 11:31
por asimoes
Tenho esse teste com uma Thread usando os parâmetros HB_BITOR

A função recebe lContinua .T., funciona.

Mas se eu quiser trocar o valor para lContinua := .F., a Thread continua com o valor da variável lContinua com .T.
Tem como intervir nos valores em uma thread?

no DO WHILE lContinua


Código: Selecionar todos

PUBLIC lContinua := .T.

        nThread := Hb_ThreadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
                                             HB_THREAD_INHERIT_PRIVATE, ;
                                             HB_THREAD_INHERIT_MEMVARS, ;
                                             HB_THREAD_MEMVARS_COPY ), ;
                                   { ||  Teste() } ) 

lContInua := .F.                                   

FUNCTION Teste()
LOCAL nRow AS NUMERIC, nCol AS NUMERIC

   nCol := Col()
   nRow := Row()
   
   nSeconds := Seconds()
   
   DO WHILE lContinua
   
      Hb_IdleSleep( 1 )

      IF (Seconds() - nSeconds) > 5
         nSeconds := Seconds()
         cTime := Time()
         IF File( "TIME.TXT" )
            cTime := Hb_MemoRead( "TIME.TXT" ) + Hb_Eol() + cTime
         ENDIF
         Hb_MemoWrit( "TIME.TXT" , cTime )
      ENDIF   
      hwg_DoEvents()
   ENDDO
   
   WAPI_BRINGWINDOWTOTOP( Hb_gtInfo( HB_GTI_WINHANDLE ) )
   
   Quit   

RETURN Nil

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 12:07
por JoséQuintas
Não, só se for PUBLIC.

Uma opção é criar classe e/ou função.

Código: Selecionar todos

FUNCTION AppUserName( cValue )

   STATIC cAppUserName := ""

   IF cValue != Nil
      cAppUserName := cValue
   ENDIF

   RETURN cAppUserName
Desse jeito, usa AppUserName(), ou AppUserName( "JOSE" ), tanto faz a thread.
Acaba sendo bom, porque a compilação vai avisar se escrever errado.

Também pode ser array.
Classe... não é tão bom, porque não vai ter checagem de nome errado.

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 12:15
por JoséQuintas
Veja se é uma coisa parecida com esta que está querendo:

Código: Selecionar todos

CREATE CLASS RunWhileThreadClass

   VAR lExit        INIT .F.
   VAR nThreadId
   VAR nInterval    INIT 600
   VAR cWindowTitle INIT ""
   VAR bCode
   METHOD New()     INLINE ::nThreadId := hb_ThreadSelf(), SELF
   METHOD Execute( bCode )

   ENDCLASS

METHOD Execute( bCode ) CLASS RunWhileThreadClass

   LOCAL nCont

   hb_gtReload( "WVG" )
   IF bCode != NIL
      ::bCode := bCode
   ENDIF
   AppInitSets()
   HB_GtInfo( HB_GTI_WINTITLE, ::cWindowTitle )
   wvgSetAppWindow():Hide()
   DO WHILE ! ::lExit
      Eval( ::bCode )
      FOR nCont = 1 TO ::nInterval
         hb_ReleaseCPU()
         IF hb_ThreadWait( ::nThreadId, 0.1, .T. ) == 1
            ::lExit := .T.
         ENDIF
         Inkey(1)
         IF ::lExit
            EXIT
         ENDIF
      NEXT
   ENDDO

   RETURN NIL
O que isso faz?
a thread vai ficar rodando, enquanto a thread que a chamou continuar ativa.
Opcionalmente, se alterar lExit := .T. ela encerra assim que possível.

Exemplo:

Código: Selecionar todos

oClasse := RunWHileThreadClass():New()
oClasse:bCode := { || Any() }
hbThreadStart( { || oClasse:Execute() } )
...
oClasse:lExit := .T.
Usei isso, por exemplo, em pedidos.
Enquanto o usuário estava na digitação de pedidos, aparecia alerta avisando de atrasos.
Saiu de pedidos, a rotina também encerrava automático. (nesse caso não precisava oClasse:lExit := .T.)

E qualquer coisa da classe, fica visível de qualquer thread.
São assim os controles Windows/GUI, só pra curiosidade.

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 12:20
por JoséQuintas
O cuidado com threads sobre compartilhar variáveis é grande.

Imagine o usuário num relatório escolhendo impressora, e em outro lugar também escolhendo impressora.
Se a variável for pública, um relatório vai alterar a impressora do outro.

Outro caso, um array:
Uma thread tá trabalhando com o array de um tamanho, e a outra thread altera esse tamanho.
E não só array, de repente uma string de tamanho 30 vira tamanho 10... se for no momento exato de pegar o conteúdo, o estrago pode ser grande, porque invade área da memória que não pertence à variável.
Nessas situações é onde precisa um cuidado a mais, o tal Mutex() que seria parecido com RLock(), mas pra variáveis.
Isso evita que o conteúdo seja alterado por outra thread durante o uso.

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 13:27
por asimoes
QUintas,

Entendi isso é muito pontual, enfim valeu pela dica

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 13:29
por asimoes
Quintas,

faltou a função AppInitSets()

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 13:52
por JoséQuintas
asimoes escreveu:faltou a função AppInitSets()
Uso essa pra configurar meu default.
Em thread precisa configurar RDDSetDefault(), SET DATE, e outras coisas, como se fosse outro EXE.
Então só criar com os seus defaults.

Nota: Usei um nome que achei adequeado, inicia os "sets" do aplicativo, por isso chamei AppInitSets()

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 16:15
por asimoes
Quintas,

Aproveitando parte da sua classe, consegui simular o que eu queria debugando pra saber o conteúdo do arquivo time.txt
A função teste recebeu a troca da variável lExit depois de executada

Código: Selecionar todos

CREATE CLASS RunWhileThreadClass

   VAR lExit        INIT .F.
   METHOD New()     INLINE SELF CONSTRUCTOR 

   ENDCLASS
   PUBLIC oClasse := RunWHileThreadClass():New()

        nThread := Hb_ThreadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
                                             HB_THREAD_INHERIT_PRIVATE, ;
                                             HB_THREAD_INHERIT_MEMVARS, ;
                                             HB_THREAD_MEMVARS_COPY ), ;
                                             { || Teste() } ) 

   Inkey(0)
   
   oClasse:lExit := .T.       
   

FUNCTION Teste()
LOCAL nRow AS NUMERIC, nCol AS NUMERIC

   nSeconds := Seconds()
      
   DO WHILE ! oClasse:lExit
   
      Hb_IdleSleep( .1 )

      IF (Seconds() - nSeconds) > 5
         nSeconds := Seconds()
         cTime := Time()
         IF File( "TIME.TXT" )
            cTime := Hb_MemoRead( "TIME.TXT" ) + Hb_Eol() + cTime
         ENDIF
         Hb_MemoWrit( "TIME.TXT" , cTime )
      ENDIF   
      hwg_DoEvents()
   ENDDO

   Quit   

RETURN Nil

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 23 Fev 2021 22:53
por JoséQuintas
Só um comentário:

Porque não uma janela executando alguma coisa?

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 10:49
por asimoes
JoséQuintas escreveu:Só um comentário:

Porque não uma janela executando alguma coisa?
O meu teste é conseguir debugar o sistema enquanto a thread está sendo executada, dessa forma eu consigo debugar enquanto a thread está rodando e atribuir oClasse:lExit := .T. a qualquer momento no meu teste por fora da thread para interromper a sua execução, nesse teste eu não preciso de janela na thread

O interessante não sei se é uma limitação ou falha só consegui interromper a thread com uma variável de classe, uma variável publica tipo public lExit := .F. e depois de executada a thread modificando para lExit para .f. , não funciona, somente com variável da classe

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 12:02
por JoséQuintas
Se é só por causa da variável, usa a outra opção.

Código: Selecionar todos

FUNCTION AbortRun( lAbort )

   STATIC sAbort := .F.

   IF lAbort != Nil .AND. lAbort
      sAbort := .T.
   ENDIF

   RETURN sAbort
Na rotina:

Código: Selecionar todos

DO WHILE .T.
   IF AbortRun()
      EXIT
   ENDIF
ENDDO
E quando precisar:

Código: Selecionar todos

AbortRun( .F. )
hb_thread(...)
...
AbortRun( .T. )
A não ser que vão ter várias situações diferentes ao mesmo tempo.

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 13:07
por asimoes
Quintas,

A sua alternativa também resolver, vlw

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 13:29
por asimoes
Aquintas,

Não funcionou

Troquei o oClasse:lExit no while por AbortRun()

Antes da chamada da Thread

AbortRun( .F. )

DO WHILE ! AbortRun() //oClasse:lExit

Depois da chamada da Thread AbortRun( .T. )

O loop da Thread continuou rodando, só funciona com oClasse:lExit

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 13:37
por asimoes
Completando:

Assim funciona, usando a função AbortRun() não

Código: Selecionar todos

  oClasse:lExit := .F.
 
   fErase( "TIME.TXT" )
   
         nThread := Hb_ThreadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
                                              HB_THREAD_INHERIT_PRIVATE, ;
                                              HB_THREAD_INHERIT_MEMVARS, ;
                                              HB_THREAD_MEMVARS_COPY ), ;
                                              { || Teste() } ) 
                                       
   altd()
   
   oClasse:lExit := .T.
13:35:06
13:35:11
13:35:16
FIM

O FIM siginifica que saiu do while

Duvida sobre Thread (Hb_ThreadStart)

Enviado: 24 Fev 2021 15:59
por Claudio Soto
Alexandre probá sin el parámetro HB_THREAD_MEMVARS_COPY

Porque si mal no recuerdo HB_THREAD_MEMVARS_COPY envía una copia de la variable (paso por valor) y no la variable en sí (paso por referencia)