c # многопоточная помощь - PullRequest
       3

c # многопоточная помощь

0 голосов
/ 10 августа 2009

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

Сообщение содержит час, метку, сообщение и тип сообщения. Этот тип сообщения я использую, чтобы показать сообщение определенным цветом.

Теперь у меня проблема в том, что иногда 2 потока выполняются одновременно, и я пытаюсь установить сообщение. Итак, я получил исключение между потоками.

Я знаю, что могу решить эту проблему с помощью делегата. Но я как бы застрял здесь.

Вот как я сейчас устанавливаю свое сообщение:

private void SetMessages()
{
   rtxtMessage.Text = "";

   foreach (var message in GlobalVariables.MessageList)
   {
      var text = message.Date.ToShortTimeString() + " " + message.Label + ": " + 
                 message.TheMessage;

      switch (message.Type)
      {
         case GlobalVariables.MessageType.normal:
             rtxtMessage.SelectionColor = Color.Black;
             break;
         case GlobalVariables.MessageType.calculation:
             rtxtMessage.SelectionColor = Color.Green;
             break;
         case GlobalVariables.MessageType.error:
             rtxtMessage.SelectionColor = Color.Red;
             break;
         case GlobalVariables.MessageType.warning:
             rtxtMessage.SelectionColor = Color.Orange;
             break;
         default:
             break;
      }
      rtxtMessage.SelectedText = text + Environment.NewLine;
      rtxtMessage.ScrollToCaret();
   }
   pnlMessage.Visible = true;
}

Итак, главный вопрос: как мне переписать это, чтобы он работал с делегатом?

Ответы [ 2 ]

2 голосов
/ 10 августа 2009

Я интерпретирую ваш вопрос так, как будто вы используете класс BackgroundWorker для многопоточной работы. Тогда тогда возникает вопрос, как сообщения попадают в коллекцию GlobalVariables.MessageList. Если это происходит в событии RunWorkerCompleted, у вас не должно быть проблем с многопоточностью, поскольку он выполняется в потоке пользовательского интерфейса (именно так я и рекомендую). Если он обновляется непосредственно из рабочего процесса, вам необходимо самостоятельно позаботиться о проблемах синхронизации и потоков (используя один из доступных механизмов блокировки).

Обновление (после ответа на комментарий в исходном вопросе):

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

private void BackgroundWorker_RunWorkerCompleted(object sender, 
                           System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.Invoke(
            new Action<object, RunWorkerCompletedEventArgs>(
                               BackgroundWorker_RunWorkerCompleted), sender, e);
    }
    else
    {
        // update the message list, and then call SetMessages()
        SetMessages();
    }
}

Тем не менее, я призываю вас точно определить, при каких обстоятельствах возникает исключение из потоков, поскольку оно не должно происходить при использовании события RunWorkerCompleted. Я провел тест, в котором одновременно запустил большое количество (несколько сотен) фоновых рабочих и не смог спровоцировать какого-либо столкновения. Он также не использовал Invoke-path в приведенном выше примере кода.

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

private void SetMessage(MyMessage message)
{
  var text = message.Date.ToShortTimeString() + " " + message.Label + ": " +             message.TheMessage;

  switch (message.Type)
  {
     case GlobalVariables.MessageType.normal:
         rtxtMessage.SelectionColor = Color.Black;
         break;
     case GlobalVariables.MessageType.calculation:
         rtxtMessage.SelectionColor = Color.Green;
         break;
     case GlobalVariables.MessageType.error:
         rtxtMessage.SelectionColor = Color.Red;
         break;
     case GlobalVariables.MessageType.warning:
         rtxtMessage.SelectionColor = Color.Orange;
         break;
     default:
         break;
  }
  rtxtMessage.SelectedText = text + Environment.NewLine;
  rtxtMessage.ScrollToCaret();

  pnlMessage.Visible = true;
}

Вы должны иметь возможность вызывать этот метод прямо из обработчика событий RunWorkerCompleted и просто передавать сообщение в метод.

0 голосов
/ 10 августа 2009

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

private void SetMessages()
{
   rtxtMessage.Text = "";
   lock(GlobalVariables.MessageList)
   {
      foreach (var message in GlobalVariables.MessageList)   
      { 
         //Rest of your code 
      }   
      pnlMessage.Visible = true;
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...