• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

almamater

BackgroundWorker

9 mensagens neste tópico

Bom.. é assim, estou a utilizar um código recursivo para me devolver pastas e ficheiros de uma determinada directoria e colocar o resultado numa CheckListBox:


Private Sub ListaFicheiros()
        Dim nameOfDirectory As String
        CLB.Items.Clear() ' CheckListBox
            
         nameOfDirectory = Label1.Text 
      
        Dim myDirectory As DirectoryInfo
        myDirectory = New DirectoryInfo(nameOfDirectory)
        WorkWithDirectory(myDirectory)
    End Sub

    Private Sub WorkWithDirectory(ByVal aDir As DirectoryInfo)
         Dim nextDir As DirectoryInfo
        WorkWithFilesInDir(aDir)
        For Each nextDir In aDir.GetDirectories
            WorkWithDirectory(nextDir)
        Next
    End Sub

    Private Sub WorkWithFilesInDir(ByVal aDir As DirectoryInfo)
             Dim aFile As FileInfo
        For Each aFile In aDir.GetFiles()
            If CLB.Items.Contains(aFile.Name) = True Then
                Else
                CLB.Items.Add(aFile.Name)
            End If
        Next
        End Sub

Acontece que qdo adiciono uma pasta com milhares de ficheiros o programa fica bloqueado até concluir o For Each.. estava a tentar adicionar um BackgroundWorker para fazer isto à parte mas dá-em sempre erro devido à variável  "aDir"...

Que sugestões têm? o meu objectivo é poder cancelar o processo sempre que quiser para n ter que aguardar...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O teu problema tem haver com CLB.Items.Add(aFile.Name)..

Ou seja.. é uma das parte mais complicadas ao trabalhar com threads.. dentro de um thread não podes interagir com objects do tipo GUI sem que antes verifiques se ele necessita de uma invocação virtual..(quase sempre necessita).. parece um pouco complicado de entender.. mas não é.. e faz sentido..

Coloca este Sub e o Delegate que irá resolver teu problema..

    Private Delegate Sub DelAddFile(ByVal sNome As String)

    Private Sub AddFile(ByVal sNome As String)
        If CLB.InvokeRequired Then
            CLB.Invoke(New DelAddFile(AddressOf AddFile), sNome)
        Else
            CLB.Items.Add(sNome)
        End If
    End Sub

E alteras o teu Sub para esta forma..

    Private Sub WorkWithFilesInDir(ByVal aDir As DirectoryInfo)
        Dim aFile As FileInfo
        For Each aFile In aDir.GetFiles()
            If CLB.Items.Contains(aFile.Name) = True Then
            Else
                AddFile(aFile.Name)
            End If
        Next
    End Sub

Este artigo esta muito bom..

http://wiki.portugal-a-programar.org/visual_basic_.net:backgroundworkers

Só falta mesmo só falta mesmo comentar a parte da interacção com os objectos do tipo GUI..

E também a forma como se faz para controlar o Thread com Start/Pause/Resume.. claro que mesmo dentro dos threads á muita coisa por onde expandir..

Compr.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Este artigo esta muito bom..

http://wiki.portugal-a-programar.org/visual_basic_.net:backgroundworkers

Só falta mesmo só falta mesmo comentar a parte da interacção com os objectos do tipo GUI..

E também a forma como se faz para controlar o Thread com Start/Pause/Resume.. claro que mesmo dentro dos threads á muita coisa por onde expandir..

Compr.

O bonito de ser uma Wiki, é que tu podes complementar isso :P

Lá, eu dou umas pistas acerca disso:

ProgressChanged é o evento disparado sempre que se ordena um relatório de progresso. É possível afectar o UIT neste sub

Não dou uma "achega" de start e stop porque de facto os backgroundworkers não os têm (não são beeeeem Threading.Thread) :P

De qualquer forma, com a combinação InvokeRequired/Invoke resolves o problema de acertar no thread de onde pode ser disparado. Edita o artigo! :)

Mesmo assim, almamater, os BackGroundWorkers papam argumentos de entrada Object, e o ReportProgress também pode deixar sair um Object.

É uma questão de imaginação já que podes transportar qualquer coisa entre threads "safely" :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

@ribeiro55

Sim tens toda a razão..

A minha ideia não foi criticar o já feito.. apenas estava a tentar ajudar.. analisando o problema..

Eu normalmente quando o utilizo Threads crio estruturas de dados com todos os objectos que irei utilizar.. e invoco ao iniciar o thread.. dá um pouco de trabalho mas fica com bom aspecto organizado ..

Mas.. fica prometido.. irei editar as partes do InvokeRequired/Invoke.. e também como controlar o thread com pause/resume/stop..

Para controlar o thread.. eu utilizo esta tecnica..

Criando uma variável do tipo Boolean.. e depois é só colocar três botões.. no Pause coloca se a variável a True.. em resume False..

Para cancelar.. já referes no teu artigo..

If BGW.CancellationPending = True Then
	e.Cancel = True
	'Aqui tens de fazer Return ou Exit For tambem.. porque assim não termina o ciclo..
	Return
Else

	'Faz um loop enquanto estiver em Pause
	Do While __ThreadPause
		Threading.Thread.Sleep(500)
	Loop

	Debug.Print("Este é o print #" & i)
	BGW.ReportProgress(CInt((i * 100) / Max))
End If

Compr. :P

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

@ribeiro55

Sim tens toda a razão..

A minha ideia não foi criticar o já feito.. apenas estava a tentar ajudar.. analisando o problema..

E fizeste tu muito bem, ora! Não sou desse tipo de pessoas :P

Está-se sempre a aprender. Sempre :)

Voltando ao tópico, almamater, diz qualquer coisa em relação ao que conseguiste ou não fazer depois das respostas. Quanto mais não seja, ajudas outros com a mesma dúvida mostrando a tua solução.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

E fizeste tu muito bem, ora! Não sou desse tipo de pessoas :P

Está-se sempre a aprender. Sempre :)

Voltando ao tópico, almamater, diz qualquer coisa em relação ao que conseguiste ou não fazer depois das respostas. Quanto mais não seja, ajudas outros com a mesma dúvida mostrando a tua solução.

Sim sim.. claro! mas eu ainda n disse nada para não chatear tanto.. pois acho que tb pode ser burrice minha e ando para aqui a inventar...

ou seja..

fiz o que o fLaSh_PT indicou... mas secalhar n percebi muito bem ou não estou a fazer correctamente.. tenho assim:

Estes Subs são os recursivos e os que fazem a aplicação bloquear e esperar que terminem:

Private Sub [b]WorkWithDirectory[/b](ByVal aDir As DirectoryInfo)
        Try
            Dim nextDir As DirectoryInfo
            WorkWithFilesInDir(aDir)
            For Each nextDir In aDir.GetDirectories
                WorkWithDirectory(nextDir)
            Next

        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical, "Erro no Processo")
        End Try
    End Sub

    Private Sub [b]WorkWithFilesInDir[/b](ByVal aDir As DirectoryInfo)
        Try
            Dim aFile As FileInfo
            For Each aFile In aDir.GetFiles()
                If CLB.Items.Contains(aFile.Name) = True Then
                Else
                    AddFile(aFile.Name)
                End If
            Next
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical, "Erro no Processo")
        End Try
    End Sub

e então tentei substituir a parte do "WorkWithFilesInDir" por um BackgroundWorker:

ficou tudo assim:

 Private Sub backgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles backgroundWorker.DoWork
        Dim aFile As FileInfo
        For Each aFile In aDir.GetFiles()
            If CLB.Items.Contains(aFile.Name) = True Then
            Else
                AddFile(aFile.Name)
            End If
        Next
    End Sub

mas dá o erro de "Error 1 - Name 'aDir' is not declared"

e estava a tentar utilizar aquilo que foi dito mas ainda não cheguei à resolução.. nem sei se estou a fazer bem  :hmm:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ok  :)  consegui resolver alterando a forma de pesquisa de pastas e ficheiros desta forma:

   Private Sub backgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles backgroundWorker.DoWork
       (...)
        GetRecursiveDir(nameOfDirectory)
    End Sub

  Private Function GetRecursiveDir(ByVal strPath As String) As Integer
        Dim objDir As New DirectoryInfo(strPath & "\")
        For Each d In objDir.GetDirectories
            AddFile(d.Name)
            GetRecursiveDir(strPath & "\" & d.Name)
           Next

        Dim objFile As FileInfo
        For Each objFile In objDir.GetFiles
            AddFile(objFile.Name)
        Next
         Return 0
           End Function

  Private Delegate Sub DelAddFile(ByVal sNome As String)
    Private Sub AddFile(ByVal sNome As String)
        If CLB.InvokeRequired Then
            CLB.Invoke(New DelAddFile(AddressOf AddFile), sNome)
        Else
            CLB.Items.Add(sNome)
        End If
    End Sub

O  problema agora é que n consigo fazer ou saber como reportar o progresso do backgroundworker.

Isto eu sei que ta bem:

Private Sub backgroundWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles backgroundWorker.ProgressChanged
        Progressbar.Value = e.ProgressPercentage
    End Sub

Mas onde e como faço o backgroundWorker.ReportProgress()? alguma dica? :D

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Desculpem, mas em vez do backgrounworker prefiro usar um thread sozinho ou um thread da threadpool.

E penso que faz a mesma coisa.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora