Report Error while copying files and folders using ThreadPool

0

I have a Windows Form application which copies files and folders to multiple machines on the network.

When the copy is not possible, the machine name is added to a list, and in the end I put it in a Listbox .

The problem is that whenever I throw an exception in one of the threads I can not give a throw Ex to add to that list.

public class Processo
{
    private ManualResetEvent _eventoReset;
    private int _threadIndex;
    private string _destino;
    private string _origem;

    public Processo(ManualResetEvent eventoReset, string origem, string destino)
    {
        this._eventoReset = eventoReset;
        this._origem = origem;
        this._destino = destino;
    }

    public void ThreadPoolCallback(Object threadContextIndex)
    {
        this._threadIndex = (int)threadContextIndex;
        ExecutaTarefas();
        this._eventoReset.Set();
    }

    private void ExecutaTarefas()
    {
        try
        {
            FileSystem.CopyDirectory(_origem, _destino, true);
        }
        catch (Exception Ex)
        {
            throw Ex;
        }            
    }
}

In the application

int numeroDeElementos = maquinas.Count();

        ManualResetEvent[] statusEventos = new ManualResetEvent[numeroDeElementos];            
        Processo[] tarefaArray = new Processo[numeroDeElementos];

        for (int i = 0; i < maquinas.Count(); i++)
        {
            try
            {
                Ping ping = new Ping();
                PingReply retorno = ping.Send(maquinas[i].pc);

                if (retorno.Status == IPStatus.Success)
                {
                    statusEventos[i] = new ManualResetEvent(false);                       
                    Processo thr = new Processo(statusEventos[i], origem, destino));

                    tarefaArray[i] = thr;                        
                    ThreadPool.QueueUserWorkItem(thr.ThreadPoolCallback, i);                          
                }
                else
                {
                    listaMaquinas.Add(string.Format("{0} Fila: {1} Posição: {2} não foi possivel conectar ao computador. ", maquinas[i].auto, maquinas[i].fila, maquinas[i].posicao));
                }
            }                
            catch (Exception ex)
            {
                listaMaquinas.Add(string.Format("{0} - {1} - Fila:{2} - Posição:{3} - Erro: {4}", maquinas[i].auto, maquinas[i].pc, maquinas[i].fila, maquinas[i].posicao, ex.Message));
            }

            progressBar1.Value = x;
            x += 1;
        }
 foreach (WaitHandle doneEvent in statusEventos)
        {
            if (doneEvent != null)
            {
                doneEvent.WaitOne();
            }
        }

        if (listaMaquinas.Count() > 0)
        {
            listBox1.DataSource = listaMaquinas;
            MessageBox.Show("As maquinas listadas não puderam ser atualizadas.");
        }
        else
        {
            MessageBox.Show("Atualização concluida com sucesso.");
        }
    
asked by anonymous 09.01.2015 / 13:01

1 answer

1

You're making some mistakes in your code - for example throwing an exception inside a thread and hoping to catch it in a catch outside of it (that's not how it works).

The code below is almost a pseudocode, just to, along with the comments I've added, give you an idea of how to proceed:

private void ExecutaTarefas()
{
    try
    {
        FileSystem.CopyDirectory(_origem, _destino, true);
    }
    catch (Exception Ex)
    {
        // não relance a exceção e nem lance nenhuma exceção
        // que você não vá tratar dentro da própria thread a não ser que
        // você assine os eventos de "Application" para tratamento de exceções em threads.

        // em vez disso, registro o erro:

        this.erro = "não foi possível copiar " + _origem + " para " + _destino
    }            
}

...

    for (int i = 0; i < maquinas.Count(); i++)
    {
        try
        {
            Ping ping = new Ping();
            PingReply retorno = ping.Send(maquinas[i].pc);

            if (retorno.Status == IPStatus.Success)
            {
                statusEventos[i] = new ManualResetEvent(false);                       
                Processo thr = new Processo(statusEventos[i], origem, destino));

                tarefaArray[i] = thr;                        
                ThreadPool.QueueUserWorkItem(thr.ThreadPoolCallback, i);                          
            }
            else
            {
                listaMaquinas.Add(string.Format("{0} Fila: {1} Posição: {2} não foi possivel conectar ao computador. ", maquinas[i].auto, maquinas[i].fila, maquinas[i].posicao));
            }
        }                
        catch (Exception ex)
        {
            // Não espere capturar aqui a exceção lançada dentro da thread pois isso não vai funcionar!
            // Provavelmente você pode eliminar esse tratamento de exceção.
        }

You can replace the loop below

    foreach (WaitHandle doneEvent in statusEventos)
    {
        if (doneEvent != null)
        {
            doneEvent.WaitOne();
        }
    }

By:

WaitHandle.WaitAll(statusEventos)

Finally, after all the threads have finished, you go through all your processes and check which ones have failed:

foreach (Processo processo: tarefaArray)
{
    if (processo.erro != null)
        listaMaquinas.Add(processo.erro);
}

I repeat that it's just a logical idea to solve the problem! Checking if the "error" property is null, for example, can be very simpleton. But the general idea is:

  • catch the exception within the thread itself;
  • record the details of the error in properties of the process that you have run as a thread;
  • Once the thread is finished, check the process properties to find out the results.
09.01.2015 / 14:11