2016-08-03 2 views
4

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

Раньше я использовал потоки для чтения данных из последовательного порта, обрабатывал эти данные в данных для записи, а затем записывал их. У меня было 1 поток данных чтения и размещение его в буфере, другой поток обработки данных из буфера и окончательный поток, пишущий его. Таким образом, если бы я нажал кнопку, я мог бы отправить дополнительные данные.

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

Подключения к последовательному порту, в случае успеха, звоните UpdateMessageBox, который асинхронный:

private void serialConnectClick(object sender, EventArgs e) 
{ 
    if (!_serialConnected) 
    { 
     _serialConnected = SerialConnect(portCombo.Text, int.Parse(baudCombo.Text)); 
     if (!_serialConnected) 
     { 
      portCombo.SelectedIndex = 0; 
      messageTextBox.Text += "Failed to connect.\r\n"; 
      return; 
     } 

     serialConnectBtn.Text = "Disconnect"; 
     serialStatusLabel.Text = "Serial: Connected"; 
     serialStatusLabel.ForeColor = Color.Blue; 
     messageTextBox.Text += "Connected\r\n"; 
     VDIportCombo.Enabled = false; 
     soundSuccess.Play(); 
     UpdateMessageBox(); // this is an async function 
    } 
} 

Это постоянно вызывает ReadLineAsync и добавляет результат в текстовое поле:

public async Task UpdateMessageBox() 
{ 
    messageTextBox.Text += "Reading data from serial."; 
    while (_serialConnected) 
    { 
     string message = await SerialReadLineAsync(serial); 
     messageTextBox.Text += message; 
    } 
} 

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

async Task<string> SerialReadLineAsync(SerialPort serialPort) 
{ 
    byte[] buffer = new byte[1]; 
    string result = string.Empty; 
    Debug.WriteLine("Let's start reading."); 

    while (true) 
    { 
     await serialPort.BaseStream.ReadAsync(buffer, 0, 1); 
     result += serialPort.Encoding.GetString(buffer); 

     if (result.EndsWith(serialPort.NewLine)) 
     { 
      result = result.Substring(0, result.Length - serialPort.NewLine.Length); 
      result.TrimEnd('\r','\n'); 
      Debug.Write(string.Format("Data: {0}", result)); 
      result += "\r\n"; 
      return result; 
     } 
    } 
} 

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

Я хочу, чтобы другой код работал в потоке пользовательского интерфейса, пока он постоянно читает, поэтому я не хочу awaitUpdateMessageBox. Если бы это был поток, я бы просто хотел, чтобы прочитанный поток работал в фоновом режиме, и я бы просто сделал myReadThread.Start(), есть ли что-то подобное для async?

EDIT: Чтобы уточнить, этот код действительно работает, но я хочу знать, является ли это «правильным» способом делать то, что я делаю.

ответ

6

Вам нужно изменить:

private void serialConnectClick(object sender, EventArgs e) 

в

private async void serialConnectClick(object sender, EventArgs e) 

... и изменить вызов UpdateMessageBox(); на:

await UpdateMessageBox().ConfigureAwait(true); 

UpdateMessageBox следует использовать ConfigureAwait(true) для того, чтобы захватить текущий контекст (UI) иначе messageTextBox.Text += message; казнить бы в другом потоке:

public async Task UpdateMessageBox() 
{ 
    messageTextBox.Text += "Reading data from serial."; 
    while (_serialConnected) 
    { 
     string message = await SerialReadLineAsync(serial).ConfigureAwait(true); 
     messageTextBox.Text += message; 
    } 
} 

В SerialReadLineAsync, вы можете изменить:

await serialPort.BaseStream.ReadAsync(buffer, 0, 1); 

... к:

await serialPort.BaseStream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); 

... потому что SerialReadLineAsync заявления не связанные к пользовательскому интерфейсу.

Общий наконечник «асинхронной весь путь» что означает, что любой метод await ев async метод также должен быть сделан async и, в свою очередь await ред. Повторите это в стеке вызовов.

+0

Ах, не знал, что смогу сделать эту функцию асинхронной! Есть ли способ, которым мне не нужно «ждать» «UpdateMessageBox»? Когда я включаю ключевое слово 'await', я получаю' IOExceptions', когда я нажимаю кнопку второй раз (который в настоящее время запрограммирован на закрытие последовательного порта) –

+0

Для любого в будущем вы можете! Просто добавьте '.ConfigureAwait (false);' поэтому, это должно быть 'UpdateMessageBox(). ConfigureAwait (false);' –

+1

Вы уверены, что можете. Просто будьте осторожны с использованием 'ConfigureAwait (false)' во время UI-метода, иначе оператор, следующий за ним, потенциально может выполняться в другом потоке. В противном случае, если вы не заботитесь о том, какой поток использовать, используйте 'ConfigureAwait (false)'. [Расскажите подробнее] (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) – MickyD

Смежные вопросы