Параллельные потоки в C # с использованием BackgroundWorker - PullRequest
0 голосов
/ 11 мая 2010

Мое приложение на C # таково, что фоновый работник используется для ожидания подтверждения некоторых переданных данных.Вот некоторый код psuedo, демонстрирующий то, что я пытаюсь сделать:

UI_thread
{
   TransmitData()
   {
      // load data for tx
      // fire off TX background worker
   }

   RxSerialData()
   {
      // if received data is ack, set ack received flag
   }
}

TX_thread
{
   // transmit data
   // set ack wait timeout
   // fire off ACK background worker
   // wait for ACK background worker to complete
   // evaluate status of ACK background worker as completed, failed, etc.
}

ACK_thread
{
   // wait for ack received flag to be set
}

В результате тайм-аут ACK BackgroundWorker истекает, а подтверждение не принимается.Я вполне уверен, что оно передается удаленным устройством, потому что это устройство не изменилось вообще, и приложение C # передает.Я изменил ack-поток с этого (когда он работал) ...

for( i = 0; (i < waitTimeoutVar) && (!bAckRxd); i++ )
{
   System.Threading.Thread.Sleep(1);
}

... на этот ...

DateTime dtThen = DateTime.Now();
DateTime dtNow;
TimeSpan stTime;

do
{
   dtNow = DateTime.Now();
   stTime = dtNow - dtThen;
}
while ( (stTime.TotalMilliseconds < waitTimeoutVar) && (!bAckRxd) );

Последний генерирует очень острое ожиданиевремя, по сравнению с первым.Однако мне интересно, мешает ли удаление функции Sleep возможности получать последовательные данные.Разрешает ли C # запускать только один поток за один раз, то есть нужно ли мне усыплять потоки в какое-то время, чтобы другие потоки могли работать?

Буду признателен за любые мысли или предложения, которые могут у вас возникнуть.Я использую Microsoft Visual C # 2008 Express Edition.Спасибо.

Ответы [ 3 ]

3 голосов
/ 11 мая 2010

Ваша "новая" версия потока RX использует 100% процессорного времени - она ​​просто работает непрерывно и никогда не спит. Помимо общей злой природы этого подхода, эти могут (хотя и не обязательно) предотвращать некоторые другие вещи, такие как получение данных, вовремя.

Для подобных сценариев ожидания обычно используется конструкция синхронизации потоков, называемая event . Вы создаете объект «событие», и поток RX ожидает его, в то время как поток обработки сигнализирует событие, когда принимается ACK.

AutoResetEvent event = new AutoResetEvent( false );

// ...
// ACK waiting thread:
event.WaitOne();
// ...

// ...
// Whatever thread actually receives the ACK
if ( /* ack received */ )
{
    // bAckRxd = true; - comment this out. Replace with following:
    event.Set();
}

Что касается того, почему именно вы не получаете ACK, мне нужно больше информации. Какой именно канал? Это последовательный порт? Сеть? Труба? Что-то еще?

3 голосов
/ 11 мая 2010

Чтобы ответить на прямой вопрос о том, позволяет ли C # запускать один поток за раз, C # не имеет ничего общего с потоками. Платформа .NET, однако, позволит вам запускать несколько (логических) потоков одновременно. То, как это действительно обрабатывается, является функцией фреймворка и ОС.

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

Затем в обратном вызове вы обработаете результат и перейдете к следующей асинхронной операции (при необходимости) или обновите пользовательский интерфейс.

1 голос
/ 11 мая 2010

Ваш код довольно потокобезопасен. Это действительно может привести к неприятностям, когда вы ожидаете, что они начнут работать. Особенно, когда вы используете потоки BGW или пула потоков, планировщик позволяет им запускаться только тогда, когда активных потоков не больше, чем у ядер ЦП. Или когда потоки "застревают" на некоторое время. Ваш застрял. Вы также, кажется, не используете их эффективно, циклы опроса сжигают много ненужных циклов ЦП.

Используйте возможности класса SerialPort, чтобы избежать этого:

  • Вам не нужен поток передачи. Драйвер последовательного порта имеет буфер, ваш вызов Write () мгновенно вернется, когда данные поместятся в буфер. Запись из основного потока в порядке.
  • Вам не обязательно нужен поток получения. Последовательный порт уже имеет один, он запускает событие DataReceived. Он может столкнуться с таймером, который вы запустили при передаче данных.
  • SerialPort уже имеет свойство ReadTimeout. Вы можете использовать его в потоке приема для тайм-аута вызова Read ().

Sleep () не мешает последовательным портам, их драйверы считывают данные с помощью аппаратных прерываний.

...