OPERA?ÃO ENTRE THREADS INV?LIDA - DELEGATE?
Pessoal,
Bom dia.
Gostaria muito da ajuda de vocês... pesquisei na internet e tudo mais, porem não achei conteúdo que me ajudasse.
Estou com problema, que na execução de uma thread preciso atualizar um DGV, porem dá o erro: [Ô][txt-color=#e80000]Operação entre threads inválida[/txt-color][Ô]
Já verifiquei em minhas pesquisas que é necessário invocar o método delegate, porem não estou conseguindo fazê-lo...
Bom, passo a baixo a estrutura do meu código (ele é pequeno, usado para importar arquivos XMLs ao DGV).
-------------------------------------
Foi criado um BackgroundWorker no form, pois desejo que quando executar a rotina de importação no DGV (são bastantes arquivos), haja a possibilidade do usuário querer cancelar a importação.
Ao clicar no botão executa o BackgroundWorker.RunWorkerAsync()
Bom pessoal... basicamente este é todo o código do meu form. Gostaria de saber... como invoco o VerProgresso que consequentemente invoca o ImportarArquivos, o qual atualiza o DGV onde ocorre o erro?
Caso pareça confuso, tento explicar novamente.
Agradeço muito desde já.
Bom dia.
Gostaria muito da ajuda de vocês... pesquisei na internet e tudo mais, porem não achei conteúdo que me ajudasse.
Estou com problema, que na execução de uma thread preciso atualizar um DGV, porem dá o erro: [Ô][txt-color=#e80000]Operação entre threads inválida[/txt-color][Ô]
Já verifiquei em minhas pesquisas que é necessário invocar o método delegate, porem não estou conseguindo fazê-lo...
Bom, passo a baixo a estrutura do meu código (ele é pequeno, usado para importar arquivos XMLs ao DGV).
-------------------------------------
Foi criado um BackgroundWorker no form, pois desejo que quando executar a rotina de importação no DGV (são bastantes arquivos), haja a possibilidade do usuário querer cancelar a importação.
Ao clicar no botão executa o BackgroundWorker.RunWorkerAsync()
Private Sub BackgroundWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
BarraProgresso.Maximum = ArquivosListBox.Items.Count [ô]tem uma barra de progresso no form para demonstrar a situação
VerProgresso(sender, e)
End Sub
Private Sub VerProgresso(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
Dim contador As Integer = 0
For Each i In ArquivosListBox.Items [ô]Aqui contém os arquivos a serem importado, ou seja, a cada item será chamado a sub ImportarArquivos( i.ToString igual ao Caminho do Arquivo)
If BackgroundWorker.CancellationPending = True Then
e.Cancel = True
Exit Sub
End If
ImportarArquivos(i.ToString)
contador += 1
BarraProgresso.Value = contador
Next
End Sub
Private Sub ImportarArquivos(Caminho As String) [ô]Aqui atualiza o DGV
[ô][...]
DGV.Rows.Add(N_NOTA, TIPO, FormatDateTime(Convert.ToDateTime(EMISSAO), DateFormat.ShortDate), CODIGO, DESCRI, NCM, CFOP, UNID, FormatNumber(Replace(QNTDE, [Ô].[Ô], [Ô],[Ô]), 6), FormatNumber(Replace(UNIT, [Ô].[Ô], [Ô],[Ô]), 6), FormatNumber(Replace(TOTAL, [Ô].[Ô], [Ô],[Ô]), 6), CHAVE) [ô]Aqui acontece o erro!
End Sub
Private Sub BackgroundWorker_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker.RunWorkerCompleted [ô]Finalizando a Thread
BarraProgresso.Value = BarraProgresso.Minimum
CancelarButton.Enabled = True
If Not e.Cancelled = True Then
MsgBox([Ô]Operação finalizada[Ô])
Else
MsgBox([Ô]Operação cancelada[Ô])
End If
End Sub
Private Sub CancelarButton_Click(sender As System.Object, e As System.EventArgs) Handles CancelarButton.Click [ô]Cancelando a Thread
If BackgroundWorker.IsBusy = True Then [ô]IsBusy (Está funcionando/ em execução)?
BackgroundWorker.CancelAsync()
End If
End Sub
Bom pessoal... basicamente este é todo o código do meu form. Gostaria de saber... como invoco o VerProgresso que consequentemente invoca o ImportarArquivos, o qual atualiza o DGV onde ocorre o erro?
Caso pareça confuso, tento explicar novamente.
Agradeço muito desde já.
Vamos ver se entendi. Você tem uma lista de arquivos que devem ter um certo processamento. Você quer uma thread separada para cada processamento com um indicador de progresso. Isso?
Não seria separada....
Executarei a thread chamando a sub VerProgresso.
Em VerProgressp:
Será lido cada item de uma lista, e então executada uma outra sub que adicionará uma linha no DGV
Será informado a minha barra de status o total de items que já foram lidos
A cada For Each lido, por ser um thread, haverá a possibilidade do usuário clicar num botão CANCELAR do form, e caso na leitura atual do for each (ou na próxima), conste que o botão CANCELAR foi acionado, é cancelada a execução da thread VerProgresso
Resumindo...
0>IMPORTAR.click
1>BackgroundWorker.RunWorkerAsync()
2>VerProgresso
2.1>Lê cada item da lista e executa a sub CarregaArquivo (a qual atualiza o DGV
2.2>Para cada leitura dos itens da lista, é verificado se o botão cancelar foi acionado, se sim, cancela essa execução
Em 2.1 está o problema, pois pelo que pesquisei, não se pode atualizar um objeto em execução de uma thread.
Consegui explicar?
Grato.
Executarei a thread chamando a sub VerProgresso.
Em VerProgressp:
Resumindo...
0>IMPORTAR.click
1>BackgroundWorker.RunWorkerAsync()
2>VerProgresso
2.1>Lê cada item da lista e executa a sub CarregaArquivo (a qual atualiza o DGV
2.2>Para cada leitura dos itens da lista, é verificado se o botão cancelar foi acionado, se sim, cancela essa execução
Em 2.1 está o problema, pois pelo que pesquisei, não se pode atualizar um objeto em execução de uma thread.
Consegui explicar?
Grato.
O que você quer:
Uma thread separada do form, atualizando um componente do form(uma barra de progresso) e um componente do form que atue na thread que está separada do form. Ou seja, é bem difÃcil de se fazer, mas não impossÃvel. E não vai ter nenhum ganho de performance com isso.
Uma thread separada do form, atualizando um componente do form(uma barra de progresso) e um componente do form que atue na thread que está separada do form. Ou seja, é bem difÃcil de se fazer, mas não impossÃvel. E não vai ter nenhum ganho de performance com isso.
Você não deve usar threads se precisa atualizar a tela na operação que quer fazer nela.
Você só deve usar threads se o que quer fazer não depende de atualizar a tela, pois sempre que precisa atualizar a tela você vai precisar fazer um Invoke e ele vai fazer o código (que foi passado para o invoke) rodar na thread do form, o que se for feito com muita frequência pode acabar deixando o processo mais lento que fazer tudo direto na thread do form.
O que você poderia fazer é carregar um List(T) ou um DataSet e depois dele carregado por completo você mostrar estes dados no DataGrid
Você só deve usar threads se o que quer fazer não depende de atualizar a tela, pois sempre que precisa atualizar a tela você vai precisar fazer um Invoke e ele vai fazer o código (que foi passado para o invoke) rodar na thread do form, o que se for feito com muita frequência pode acabar deixando o processo mais lento que fazer tudo direto na thread do form.
O que você poderia fazer é carregar um List(T) ou um DataSet e depois dele carregado por completo você mostrar estes dados no DataGrid
Ocelot,
Boa tarde.
Acredito que entendi o que você disse e a solução que me propôs. Mas também há a necessidade de deixar o form [Ô]livre[Ô] caso o usuário pretenda cancelar o processo, visto que pode chegar a 20.000 arquivos a serem importados.
Dúvida: até onde sei, para poder interromper um processo em andamento, somente caso ele esteja sendo executado por uma thread, pois caso não esteja, só posso executar outro comando após o processo finalizar.... ou não?
Grato.
Boa tarde.
Acredito que entendi o que você disse e a solução que me propôs. Mas também há a necessidade de deixar o form [Ô]livre[Ô] caso o usuário pretenda cancelar o processo, visto que pode chegar a 20.000 arquivos a serem importados.
Dúvida: até onde sei, para poder interromper um processo em andamento, somente caso ele esteja sendo executado por uma thread, pois caso não esteja, só posso executar outro comando após o processo finalizar.... ou não?
Grato.
Não. Você pode estabelecer uma variável e consultá-la a cada interação. Se o valor estiver false para o loop usando return
Completando o que o Kerplunk disse, seria possÃvel fazer assim se você usar o Application.DoEvents no loop, sem precisar de Threads, a menos que tenha algo que você faça no loop que seja muito demorado isso pode funcionar.
Senhores,
Achei uma solução!
Não sei se é exatamente o que vcs me recomendaram, mas fiz da seguinte forma:
Continua sendo uma thread...
0>IMPORTAR.click
1>BackgroundWorker.RunWorkerAsync()
2>VerProgresso
2.1>Lê cada item da lista e executa a sub CarregaArquivo (a qual atualiza o DGV - [txt-color=#007100]aqui substitui a atualização direta no DGV, por uma varÃavel List of de uma classe que criei com propertys referentes[/txt-color])
2.2>Para cada leitura dos itens da lista, é verificado se o botão cancelar foi acionado, se sim, cancela essa execução
2.3> ativei o Reports do BackgroundWorder, e pedi para que ele retornasse o status para o progress bar...
Ficou assim o código:
ACIONANDO A THREAD
EXECUÇÃO DA SUB...
AQUI O PROCESSO DE ATUALIZAR O DGV FOI SUBSTITUÃDO POR PREENCHIMENTO DE UMA VARIÃVEL DE OUTRA CLASSE...
INFORMAÇÃO DO PROGRESSO NA PROGRESS BAR
Quando finaliza a thread, aà sim o DGV é preenchido
Caso seja clicado no botão CANCELAR
Enfim, não sei se é o que vcs me disseram, ou se é o mais correto.... mas é uma alternativa!
Obrigado à todos!!!!
Achei uma solução!
Não sei se é exatamente o que vcs me recomendaram, mas fiz da seguinte forma:
Continua sendo uma thread...
0>IMPORTAR.click
1>BackgroundWorker.RunWorkerAsync()
2>VerProgresso
2.1>Lê cada item da lista e executa a sub CarregaArquivo (
2.2>Para cada leitura dos itens da lista, é verificado se o botão cancelar foi acionado, se sim, cancela essa execução
2.3> ativei o Reports do BackgroundWorder, e pedi para que ele retornasse o status para o progress bar...
Ficou assim o código:
ACIONANDO A THREAD
Private Sub BackgroundWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
VerProgresso(sender, e)
End Sub
EXECUÇÃO DA SUB...
Private Sub VerProgresso(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
Dim contador As Integer = 0
For Each i In ArquivosListBox.Items
If BackgroundWorker.CancellationPending = True Then
e.Cancel = True
Exit Sub
End If
BackgroundWorker.ReportProgress(contador) [ô]aqui informo o contador, que será referência de quantos arquivos foram lidos
ImportarArquivos(i.ToString)
contador += 1
Threading.Thread.Sleep(50)
Next
End Sub
AQUI O PROCESSO DE ATUALIZAR O DGV FOI SUBSTITUÃDO POR PREENCHIMENTO DE UMA VARIÃVEL DE OUTRA CLASSE...
Private Sub ImportarArquivos(Caminho As String)
[...]
Dim lNFE As New NFExml
lNFE.CODIGO = CODIGO.Trim()
lNFE.DESCRI = DESCRI.Trim()
lNFE.NCM = NCM.Trim()
lNFE.CFOP = CFOP.Trim()
lNFE.UNID = UNID.Trim()
lNFE.QNTDE = QNTDE.Trim()
lNFE.UNIT = UNIT.Trim()
lNFE.TOTAL = TOTAL.Trim()
lNFE.EMISSAO = EMISSAO.Trim()
lNFE.CHAVE = CHAVE.Trim()
lNFE.N_NOTA = N_NOTA.Trim()
lNFE.TIPO = TIPO.Trim()
RetornoX.Add(lNFE)
[...]
End Sub
INFORMAÇÃO DO PROGRESSO NA PROGRESS BAR
Private Sub BackgroundWorker_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker.ProgressChanged
BarraProgresso.Value = e.ProgressPercentage
End Sub
Quando finaliza a thread, aà sim o DGV é preenchido
Private Sub BackgroundWorker_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker.RunWorkerCompleted
BarraProgresso.Value = BarraProgresso.Minimum
NFEDataGridView.Visible = True
AguardeGroupBox.Visible = False
NFEDataGridView.DataSource = RetornoX
If Not e.Cancelled = True Then
MsgBox([Ô]Operação finalizada[Ô])
Else
MsgBox([Ô]Operação cancelada[Ô])
End If
End Sub
Caso seja clicado no botão CANCELAR
Private Sub CancelarButton_Click(sender As System.Object, e As System.EventArgs) Handles CancelarButton.Click
If BackgroundWorker.IsBusy = True Then [ô]IsBusy (Está funcionando/ em execução)?
BackgroundWorker.CancelAsync()
End If
End Sub
Enfim, não sei se é o que vcs me disseram, ou se é o mais correto.... mas é uma alternativa!
Obrigado à todos!!!!
Tópico encerrado , respostas não são mais permitidas