backgroundworker выдает исключение между потоками, когда ProgressChanged? - PullRequest
0 голосов
/ 05 марта 2012

У меня есть приложение для передачи файлов (сервер-клиент) TCP [.net 4], почему backgroundworker выдает мне исключение в (backgroundWorker1_ProgressChanged)

Команда отправки клиента, которая имеет путь назначения (где файл должен быть сохранен) и размер файла на сервер, чтобы начать получать этот файл ... поэтому, как только сервер примет эту команду ... он вызовет:

fileTransfer1.Receive(destPath, fileSize);

этот метод выполняется в своем собственном потоке в form1.cs:

private void Job(object o)
    {

        Socket client = (Socket)o;
        NetworkStream stream = new NetworkStream(client);
        StreamReader sr = new StreamReader(stream);

        string cmd = null;

        while ((cmd = sr.ReadLine()) != null)
        {
            string[] command = cmd.Split('<');
            switch (command[0])
            {
               case "receive":
                            fileTransfer1.Receive(command[1], Convert.ToInt64(command[2]));
                            break;
                            default:
                            break;
            }
        }

fileTransfer1 (userControl) :

    public void Receive(string destPath, long fileSize)
    {
            List<object> job = new List<object>();
            job.Add(destPath);
            job.Add(fileSize);
            backgroundWorker1.RunWorkerAsync(job);
    }

    long sum = 0;
    long fileSize = 0;   //this will equal the size of the file later.
    private void ReceiveFile(string destPath, long fileSize)
    {
        using (fs = new FileStream(destPath, FileMode.Create, FileAccess.Write))
        {
            try
            {
                int count = 0;
                data = new byte[packetSize];
                while (sum < fileSize)
                {
                    count = network.Read(data, 0, packetSize);
                    fs.Write(data, 0, count);
                    sum += count;
                    backgroundWorker1.ReportProgress((int)((sum * 100) / fileSize));
                }
            }
            finally
            {
                CloseTransfer();
            }
        }
    }

вот метод backgroundworker:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
         List<object> job = (List<object>)e.Argument;
         string destPath = (string)job[1];
         long fileSize = (long)job[2];
         ReceiveFile(destPath, fileSize);
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBarFile.Position = e.ProgressPercentage;
        progressBarFile.Text = progressBarFile.Position.ToString() + "%";
  /*here*/  labelRemaining.Text = CnvrtUnit(fileSize - sum);  
    }

моя проблема в том, что я получил исключение (операция с несколькими потоками недопустима: доступ к элементу управления labelRemaining осуществляется из потока, отличного от потока, в котором он был создан.) , который ссылается на эту строку:

 labelRemaining.Text = CnvrtUnit(fileSize - sum);

я знаю об этом исключении и знаю, что мне нужно использовать (делегировать / вызывать) вещь ... но .. я знаю, что фоновый работник предназначался для этого .. плюс .. у меня такой же кодно сервер ейe Отправьте файл клиенту ... и он не выдаст это исключение ... он работает нормально, и форма показывает мне детали в порядке.
Так почему я получаю это исключение именно тогда, когда яполучить файл ??
примечание: метод приема работает нормально.

Ответы [ 2 ]

2 голосов
/ 05 марта 2012

Вы получаете эту ошибку, потому что backgroundWorker1_ProgressChanged() работает в потоке, отличном от потока, создавшего элемент управления labelRemaining.Предполагая, что вы создали элемент управления labelRemaining в главном потоке пользовательского интерфейса, вам необходимо:

  • Выполнить backgroundWorker1.RunWorkerAsync(job) из основного потока.

    или

  • Внутри backgroundWorker1_ProgressChanged используйте Dispatcher для выполнения изменения вашего labelRemaining объекта в главном потоке, как описано здесь

Обновление: я вижу, что вы используете winforms, а не WPF.Вместо использования Dispatcher это должно работать для вас: Как обновить графический интерфейс из другого потока в C #?

0 голосов
/ 05 марта 2012

Я понял, что

с тех пор, как мой метод получения команд Void Job (объект o) выполняется в потоке .. так что это:

case "receive":
           fileTransfer1.Receive(command[1], Convert.ToInt64(command[2]));
           break;

также выполняется в том же потоке .. это означает, что остальная часть кода также выполняется в этом потоке .. поэтому я просто изменил эту строку на эту:

case "receive":
       Action a = () => fileTransfer1.Receive(command[2], Convert.ToInt64(command[3])); 
       Invoke(a);
       break;

теперь UI-Thread будет уведомлен о запускеостальная часть получающего кода .. которая решила мою проблему.

...