Falarei hoje sobre Variáveis Globais (Global Variables) e Threads. Usarei como primeiro exemplo o programa u_sudoku.prg depois escreverei dois estudos de caso.
Conhecemos as variáveis de escopo Public, Private, Static e Local. As variáveis Public e Private podem ser declaradas em qualquer parte do programa. As variáveis Static podem ser externas ou internas. Externas, quando declaradas fora de qualquer Function ou Procedure e no cabeçalho do programa em que estão sendo declaradas. E, internas, quando atreladas à uma Procedure ou Function e logo após a declaração das variáveis de escopo Local. Já as variáveis de escopo Local apenas depois da declaração da Procedure ou Function e antes de qualquer procedimento.
As variáveis Private tem um tratamento especial, elas podem ser declaradas de três formas:
Explicitamente:
Private nPrivateVar
Implicitamente, atribuindo-se um valor a uma variável não declarada anteriormente, como por exemplo:
IF ( Type("nPrivateNaoDeclarada") == "U" )
nPrivateNaoDeclarada := "Terei o escopo Private e a partir desse ponto todo mundo me vê"
ENDIF
Ou, ainda, através da função _SetOwnerPrvt( cVar , uValue ) uma grande sacada da microsiga. _SetOwnerPrvt() é interessante pois a Private declarada à partir dela pode ser acessada pela função um nível acima de onde foi declarada. Veja como é interessante.
User Function Foo()
Private nPrvtFoo := 10
TestSetOwp()
U_Foo() não tem acesso a variável Publica declarada Explicitamente em uma rotina de nível inferior.
Type("nPrvtNoSetOwp") -> "U"
Aqui U_Foo() tem acesso à variável nPrvtSetOwp declarada pela _SetOwnerPrvt()
Type("nPrvtSetOwp") -> "N"
Return( NIL )
Static Function TestSetOwp()
Neste Ponto TestSetOwp() tem acesso à variável Private declarada um nível acima
Type("nPrvtFoo") -> "N"
Declarando dessa forma todas as rotinas a partir deste ponto terão acesso e a rotina chamadora também terá acesso à esse variável
_SetOwnerPrvt( "nPrvtNoSetOwp" , 1512 )
Type("nPrvtNoSetOwp") -> "N"
Declarando dessa forma apenas as rotinas chamadas à partir desse ponto terão acesso
Private nPrvtNoSetOwp := 1215
Type("nPrvtNoSetOwp") -> "U"
Return( NIL )
O Problema dessas variáveis é que elas só estão disponíveis dentro da Thread que as criou, se necessitarmos ter acesso ao conteúdo dessar variáveis em outra Thread não será possível. É aí que entra a variável se escopo Global. Para manipularmos as variáveis de escopo Global teremos que usar as seguintes funções da API:
Para executar o Lock nas Global
GlbLock()
Para Declarar uma Variável e/ou Atribuir um novo valor
PutGlbValue( cVar , cValue )
Para liberar o Lock nas Global
GlbUnlock()
Para obter os valores das Global.
GetGlbValue( cVar )
Um pequeno fragmanto do código u_sudoku.prg modificado para exemplificar o uso da variável de escopo Global
Tento obter a exclusividade na pilha variáveis de escopo Global
While !( GlbLock() )
Sleep(5)
End While
Se a variavel bStartSudoku não estiver declarada, declaro-a, setanto seu valor Inicial
PutGlbValue( "bStartSudoku" , "1" )
Se a variavel cSudokuTime não estiver declarada, declaro-a, setanto seu valor Inicial
PutGlbValue( "cSudokuTime" , "00:00:00" )
Libero o Lock na pilha de variáveis de escopo Global
GlbUnlock()
Executo StartJob para a criação de uma nova Thread
StartJob( "U_SudokuExec" , GetEnvServer() , .F. , "SudokuTime" , { Time() , 1 } )
Tanto bStartSudoku quanto cSudokuTime poderão ser lidas através de GetGlbValue(cVar) e modificadas, utilizando PutGlbValue(cVar,cVal), por qualquer Thread criada à partir de sua declaração e terão validade até que a última Thread seja liberada.
Saudações Naldo,
ResponderExcluirSobre variaveis de escopo STATIC, tenho um arquivo fisico que contem abaixo dos INCLUDES variaveis estaticas declaradas, por exemplo:
#include 'Protheus.ch'
STATIC xBarrType := Iif(IsSrvUnix(),'/','\')
.
.
.
Varias U_Functions => (UFn)
.
.
.
Minha duvida é, se ao chamar uma UFn nesse fonte
a variavel STATIC sera iniciada também?
Abs,
Sergio
Isso mesmo....
ResponderExcluirCara a função _SetOwnerPrvt() é a maior sacada com certeza... eu sempre me ferrava nos pontos de entrada... com ela acabou as gambiarras !!! vlww
ResponderExcluirPessoal, boa noite.
ResponderExcluirEu estou usando esse conceito para resolver um problema aqui, mas travei na seguinte situação:
Consigo carregar uma tabela em uma Thread e ler em outra ? Ou seja:
Na função A, inicio a função B como JOB. Nessa função B, eu carrego uma tabela com o TcGenQry, no alias "view1".
Gostaria de ler os campos dessa "view1" na função A.
É possivel?
Não. Não é possível por causa da forma que threads foram implementadas no sistema. Exceto pelas variáveis do tipo Global, nenhuma outra é compartilhada entre threads. Mas a solução do seu problema é simples: crie uma tabela temporária a partir da sua view e ela poderá ser acessada por qualquer thread.
Excluir...
ResponderExcluirVlw Naldo. Brigado mesmo.
ResponderExcluirAgora só mais uma forcinha. Sabe qual melhor forma de fazer isso, pensando na performance? Essa rotina em questão eh um relatório, que já leva horas para carregar a view, e a ideia seria não dobrar esse tempo ao jogar em uma tabela temporaria...
Já tentou extrair os dados (criando uma tabela) via Procedure???
ExcluirConsegui um resultado muito bacana com as temporárias globais do sql mesmo. Assim que terminar posto o resultado completo aqui.
ResponderExcluir