Фоновый рабочий и межпоточная проблема - PullRequest
3 голосов
/ 12 июня 2011

У меня есть приложение Winforms, которое работает нормально .. использую BackgroundWorkerThread для управления удобством использования графического интерфейса пользователя при обработке последовательных данных на устройстве.

Работает нормально.

Теперь ядобавление нового метода и копирование того, что я делал в других формах.Но я получаю исключение кросс-потока.

Я объявляю свой BWT следующим образом:

BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += DownloadGpsDataFromDevice;
            bw.WorkerReportsProgress = true;
            bw.RunWorkerAsync();

Затем у меня есть метод, отлаженный как этот, который работает в фоновом режиме:

private void DownloadGpsDataFromDevice(object sender, DoWorkEventArgs e)
{
    _performScreenUpdate = true;
    tsStatus.Text = "Downloading GPS Data...";
    Invalidate();
    Refresh();


    Common.WriteLog("Extracting raw GPS data. Sending LL.");

    ReplyString raw = DeviceServices.ExecuteCommand("$LL");

DeviceServices.ExecuteCommand ("$ LL");это бит, который делает работу, но я получаю исключение в предыдущей строке, где я регистрируюсь в текстовом файле.Теперь, это заставляет вас беспокоиться - запись в файл.Тем не менее, я делал это тысячи раз в другом BWT.

Я сделал поток записи безопасным.Вот мой метод Common.WriteLog:

public static void WriteLog(string input)
{
    lock (_lockObject)
    {
        WriteLogThreadSafe(input);
    }
}

private static void WriteLogThreadSafe(string input)
{
    Directory.CreateDirectory(LogFilePath);
    StreamWriter w = File.AppendText(LogFilePath + @"\" + LogFileName);
    try
    {
        w.WriteLine(string.Format("{0}\t{1}", DateTime.Now, input));
    }
    catch (Exception e)
    {
        System.Console.WriteLine("Error writing to log file!");
        System.Console.WriteLine("Tried to write: [" + input + "]");
        System.Console.WriteLine("Failed with error: [" + e.Message + "]");
    }
    finally
    {
        w.Close();
    }

}

Это работало целую вечность.Я не верю, что ошибка есть.Я думаю, я просто что-то упускаю во время разговора?

Ответы [ 4 ]

6 голосов
/ 12 июня 2011

Вы не можете изменять элементы пользовательского интерфейса из потока BackgroundWorker.Вам придется выполнить маршалинг обратно в поток пользовательского интерфейса, вызвав Invoke().

Попробуйте это

private void DownloadGpsDataFromDevice(object sender, DoWorkEventArgs e)
{
    _performScreenUpdate = true;
    Invoke((MethodInvoker)(() => {
             tsStatus.Text = "Downloading GPS Data...";
             Invalidate();
             Refresh();
     });
     ...
0 голосов
/ 12 июня 2011

Вы уверены, что это правильная линия? Я не думаю, что вы должны быть в состоянии обновить пользовательский интерфейс в вашем работнике. Попробуйте закомментировать обновление графического интерфейса, очистить и собрать свое решение, чтобы увидеть, действительно ли проблема с регистрацией. Чтобы обновить пользовательский интерфейс, установите WorkerReportsProgress и создайте для него обработчик событий, чтобы обновить пользовательский интерфейс и сообщить о ходе работы на рабочем месте.

0 голосов
/ 12 июня 2011

Некоторые экземпляры не могут или не должны быть доступны для нескольких потоков. У вас есть два варианта защиты данных от исключений между потоками.

Вы можете заблокировать свой объект при доступе к нему из нескольких потоков с помощью блокировки:

объект locker = новый объект ();

SomeObject MyObject = new SomeObject ();

private void FromMultipleThread()
{
    lock(locker)
    {
        MyObject = OtherObject;
    }
}

Ваш второй вариант - заблокировать ваш поток с помощью ManualResetEvent. Это очень просто, вам нужно всего лишь вызвать WaitOne () из вашего ManualResetEvent, чтобы заблокировать ваш поток, в то время как другой поток обращается к вашему «перекрестному» объекту.

В вашем случае вы хотели бы изменить свой пользовательский интерфейс из reportProgress вашего backgroundWorker. ReportProgress вернется в исходный поток, после чего вы сможете изменить свой пользовательский интерфейс.

0 голосов
/ 12 июня 2011

Проблема в том, что вы обновляете элементы пользовательского интерфейса из потока без пользовательского интерфейса:

Эти строки не должны быть внутри DownloadGpsDataFromDevice

tsStatus.Text = "Downloading GPS Data...";
Invalidate();
Refresh();

Чтобы воспользоваться BackgroundWorker, запустите метод bw.ReportProgress(0);. Обновите пользовательский интерфейс в обработчике ProgressChanged, который был специально разработан для этой цели.

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (e.ProgressPercentage = 0)
    {
        tsStatus.Text = "Downloading GPS Data...";
        Invalidate();
        Refresh();
    }
}
...