Página 1 de 3

Rotina em Segundo Plano - Thread

Enviado: 25 Mar 2017 01:13
por cjp
Uma dúvida: thread só funciona na minigui? Eu uso modo console; é possível usar thread?

Rotina em Segundo Plano

Enviado: 25 Mar 2017 10:25
por fladimir
Sim é possível

Rotina em Segundo Plano

Enviado: 25 Mar 2017 11:35
por cjp
Tem como vc me ensinar a fazer isso, ou me passar algum link onde ensina? Eu nunca usei e não faço ideia de como começar. Já li alguma coisa aqui, mas sempre voltado à Minigui.

Rotina em Segundo Plano

Enviado: 25 Mar 2017 16:40
por fladimir

Rotina em Segundo Plano

Enviado: 25 Mar 2017 17:46
por cjp
Testei e funcionou.

Mas, sinceramente, acho que ainda não entendi a ideia.

Pensei que a ideia da thread seria a rotina ficar rodando em segundo plano, sem aparecer na tela. Não é isso? Pelo menos não é isso que está acontecendo no caso aqui. Ele simplesmente desviou para a função que era pra ficar rodando em segundo plano, aparecendo normalmente na tela.

Em suma, não entendi qual a diferença para uma chamada normal da função, nem qual a vantagem dessa aplicação.

Poderia me explicar, por favor?

Rotina em Segundo Plano

Enviado: 25 Mar 2017 18:15
por Claudio Soto
Este talvez sea un ejemplo más claro:

Código: Selecionar todos

// Multi-Thread Demo WITH INHERIT PUBLIC vars, contrib by Roberto Lopez, March 2017

// NOTE: This is a classic ( conventional ) way how to use Multi-Thread at level of the OS, share ( inherit ) public and static vars

#include <hmg.ch>
#include "hbthread.ch"

FUNCTION Main

aThread := { NIL, NIL }

IF !hb_mtvm()
   MSGSTOP("There is no support for multi-threading")
   QUIT
ENDIF

DEFINE WINDOW Form_1 AT 301 , 503 WIDTH 550 HEIGHT 350 MAIN

    DEFINE BUTTON Button_1
        ROW    10
        COL    10
        WIDTH  160
        HEIGHT 28
        ACTION ( aThread[ 1 ] := hb_threadStart( HB_THREAD_INHERIT_PUBLIC , @Show_Time() ) )
        CAPTION "Start Clock Thread"
    END BUTTON

    DEFINE BUTTON Button_2
        ROW    50
        COL    10
        WIDTH  160
        HEIGHT 28
        ACTION ( aThread[ 2 ] := hb_threadStart( HB_THREAD_INHERIT_PUBLIC , @Show_Progress() ) )
        CAPTION "Start ProgressBar Thread"
    END BUTTON

    DEFINE LABEL Label_1
        ROW    10
        COL    360
        WIDTH  120
        HEIGHT 24
        VALUE "Clock Here!"
    END LABEL

    DEFINE PROGRESSBAR ProgressBar_1
        ROW    250
        COL    360
        WIDTH  150
        HEIGHT 30
        RANGEMIN 1
        RANGEMAX 10
        VALUE 0
    END PROGRESSBAR

    DEFINE BUTTON Button_3
        ROW    170
        COL    140
        WIDTH  260
        HEIGHT 28
        ACTION button_3_action()
        CAPTION "UI Still Responding to User Events!!!"
    END BUTTON

    DEFINE BUTTON Button_4
        ROW    250
        COL    10
        WIDTH  160
        HEIGHT 28
        ACTION button_4_action()
        CAPTION "Terminate all Threads"
    END BUTTON
    
END WINDOW

Form_1.Center
Form_1.Activate

Return


FUNCTION Show_Time()
   Form_1.button_1.enabled := .F. // disable 'Start Thread' button to avoid accidentally start a new thread!
   DO WHILE .T.   // thread infinite loop
      Form_1.label_1.value := Time()
      hb_idleSleep( .5 )
   ENDDO
RETURN nil


FUNCTION Show_Progress()
   Form_1.button_2.enabled := .F. //  disable 'Start Progress' button to avoid accidentally start a new thread!
   DO WHILE .T.   // thread infinite loop
      nValue := Form_1.progressBar_1.value
      nValue ++
      if nValue > 10
         nValue := 1
      endif
      Form_1.progressBar_1.value := nValue
      hb_idleSleep( .5 )
   ENDDO
RETURN nil


FUNCTION button_3_action
   MSGINFO('Clock and ProgressBar keep updating even the main thread is stopped at this MsgInfo!!!')
Return Nil


FUNCTION button_4_action
LOCAL i
   FOR i := 1 TO HMG_LEN( aThread ) 
      IF aThread[ i ] <> NIL
         hb_threadDetach( aThread[ i ] )   // close thread handle
         hb_threadQuitRequest( aThread[ i ] )   // terminate thread
         aThread[ i ] := NIL
      ENDIF
   NEXT
   Form_1.progressBar_1.value := 0
   Form_1.label_1.value := "Clock Here!"

   Form_1.button_1.enabled := .T.
   Form_1.button_2.enabled := .T.
Return Nil


Rotina em Segundo Plano

Enviado: 25 Mar 2017 19:13
por JoséQuintas
Talvez com GTWVG fique mais claro isso.
Pode ou não ter tela.

Código: Selecionar todos

PROCEDURE Main

   hb_ThreadStart( { || Teste() } )
   hb_ThreadStart( { || Teste() } )
   hb_ThreadWaitForAll()

   RETURN

FUNCTION Teste()

   hb_gtReload( "WVG" )
   cVar := Space(10)

   SetMode( 25, 80 )
   CLS
   @ 2, 20 SAY "teste" GET cVar
   READ

   RETURN NIL
Na prática vão ter TRES rotinas.
A principal, invisível, e mais duas com tela, totalmente independentes.

IMPORTANTÍSSIMO:
Na thread pode ser necessário configurar SET DATE, RddSetDefault(), etc.
É realmente como se fosse um programa em separado.
Mas se a Main() encerrar, todas as threads se encerram, por isso o uso de hb_ThreadWaitForAll().

E por último:
A janela GTWVG pode ser útil mesmo com outras GUIs, podendo ser usada invisível.
Não sei se isso permitiria usar o debug normal do Harbour em ambiente GUI...
Mas se misturar multithread nisso... talvez desse.... rs

Rotina em Segundo Plano

Enviado: 25 Mar 2017 19:32
por cjp
Quintas, teu exemplo não funcionou aqui. Ele compila e teoricamente roda, mas não acontece nada; não aparece tela alguma, e aparentemente nem executa a function teste().

Claudio, ainda vou testar o teu exemplo.

Rotina em Segundo Plano

Enviado: 25 Mar 2017 19:40
por JoséQuintas
Importante também na compilação:

HBMK2 test -mt gtwvg.hbc

o -mt indica multithread.
test.png

Rotina em Segundo Plano

Enviado: 25 Mar 2017 20:39
por cjp
Ah, sim, agora funcionou.

Mas, ao colocar na minha aplicação, está dando erro de variável us inexistente, embora a variável us esteja predefinida como public.

Como faço para a thread ficar invisível?

Outra coisa: em testes feitos num passado já distante de tela oculta (não era com thread, e acho que era no xharbour), quando dava erro na tela oculta, a tela principal travava. Isso ocorre também com thread?

Rotina em Segundo Plano

Enviado: 25 Mar 2017 20:42
por cjp
Mais uma coisa, por favor: usando a gtwvg, mesmo sem usar a thread, minha tela ficou cortada. Veja o anexo.

Além disso, quando abro o programa, mesmo a tela principal abre numa segunda tela, ficando a primeira tela toda preta. Por que isso?

Rotina em Segundo Plano

Enviado: 25 Mar 2017 21:56
por JoséQuintas
Pra deixar GTWVG como default:

Código: Selecionar todos

PROCEDURE hb_GTSYS()

   REQUEST HB_GT_WVG_DEFAULT

   RETURN
Provavelmente tem mais a GT console por detrás.

E não precisa hb_gtReload("WVG"), isso é só pra thread adicional ter janela adicional

Em multithread, o default é que variáveis não são passadas pras threads, nem mesmo as públicas.
Caso precise disso, a opção é usar parâmetros.

Por exemplo:

Código: Selecionar todos

// Inherit copy of public
// hb_threadJoin( hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, HB_THREAD_MEMVARS_COPY ), @thFunc() ) )

// ? "Inherit copy of privates."
// hb_threadJoin( hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PRIVATE, HB_THREAD_MEMVARS_COPY ), @thFunc() ) )

// ? "Inherit copy of publics and privates."
// hb_threadJoin( hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_MEMVARS, HB_THREAD_MEMVARS_COPY ), @thFunc() ) )


Rotina em Segundo Plano

Enviado: 25 Mar 2017 23:34
por cjp
Não deu certo.

Fiz assim:

Código: Selecionar todos

function main
  hb_ThreadStart( { || Teste("I","Inacio","25/03/17","comin") } )
  hb_ThreadWaitForAll()

   REQUEST HB_GT_WVG_DEFAULT
...
return

FUNCTION Teste()
         parameters us, nmus, vers, seis

   cVar := Space(10)
         cadativ("Teste","Teste: início"," ",0,us,0,0," ")

   SetMode( 25, 80 )
   CLS
   @ 2, 20 SAY "teste" GET cVar
   READ

   RETURN NIL

Travou tudo no início.

Rotina em Segundo Plano

Enviado: 26 Mar 2017 02:23
por JoséQuintas
Confundiu tudo.

Código: Selecionar todos

PROCEDURE Main

 hb_ThreadStart( { || Teste() } )
 hb_ThreadStart( { || Teste() } )
 hb_ThreadWaitForAll()

 RETURN

FUNCTION Teste()

 hb_gtReload( "WVG" )
 cVar := Space(10)

 SetMode( 25, 80 )
 CLS
 @ 2, 20 SAY "teste" GET cVar
 READ

 RETURN NIL

PROCEDURE hb_GTSYS()

 REQUEST HB_GT_WVG_DEFAULT

 RETURN

Rotina em Segundo Plano

Enviado: 26 Mar 2017 02:28
por JoséQuintas
Ou vamos em partes:

definir GTWVG como default, exatamente deste jeito, sem mudanças.

Código: Selecionar todos

PROCEDURE hb_GTSYS()

 REQUEST HB_GT_WVG_DEFAULT

 RETURN
uma sub-rotina pra ter tela isolada da principal

Código: Selecionar todos

FUNCTION Teste()

 hb_gtReload( "WVG" ) // isto gera tela isolada, com tela/teclado próprio
 cVar := Space(10)

 SetMode( 25, 80 )
 CLS
 @ 2, 20 SAY "teste" GET cVar
 READ

 RETURN NIL
E rotina principal, sem tela, aguardando as threads encerrarem

Código: Selecionar todos

PROCEDURE Main

 hb_ThreadStart( { || Teste() } )
 hb_ThreadStart( HB_THREAD_INHERIT_PUBLIC, HB_THREAD_MEMVARS_COPY, { || Teste() } ) // variáveis públicas visíveis na thread
 hb_ThreadWaitForAll()

 RETURN