Как отменить NetworkStream.ReadAsync без закрытия потока

Я пытаюсь использовать NetworkStream.ReadAsync() для чтения данных, но не могу найти, как отменить вызов ReadAsync() после его вызова. Для фона NetworkStream предоставляется мне подключенным объектом BluetoothClient (из библиотеки 32Feet.NET Bluetooth).

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

int bytesRead;

while (this.continueReading)
{
    bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length);

    Console.WriteLine("Received {0} bytes", bytesRead);
}

Console.WriteLine("Receive loop has ended");

Код отлично работает, получая данные, и остановит цикл, если для флага continueReading установлено значение false и данные получены, но до тех пор, пока данные не будут получены, он не продолжит работу после строки ReadAsync(). Я не вижу, как прервать вызов без получения данных.

Я знаю, что существует перегрузка ReadAsync, которая предоставляет CancellationToken, но похоже, что, поскольку NetworkStream не переопределяет поведение ReadAsync по умолчанию, токен игнорируется (см. NetworkStream.ReadAsync с токеном отмены никогда не отменяется).

Я попытался закрыть базовый поток, и это приводит к тому, что ожидающий вызов ReadAsync вызывает исключение ObjectDisposedException, и базовое соединение Bluetooth также закрывается. В идеале я не хочу полностью разрывать соединение с устройством только для того, чтобы перестать читать. Это не кажется чистым способом сделать это, и кажется ненужным разрывать весь поток только для того, чтобы прервать вызов va ReadAsync().

Любой совет?


person Swampie    schedule 07.03.2013    source источник


Ответы (3)


Вы можете реализовать асинхронную оболочку вокруг NetworkStream.Read (или ReadAsync), которая также получает токен отмены, который вы можете отслеживать и учитывать самостоятельно. Что-то вроде этого:

Task MyCancelableNetworkStreamReadAsync(NetworkStream stream, CancellationToken ct)
{
...
if(this.stream.CanRead)
{
  do 
  {
    //check ct.IsCancellationRequested and act as needed
    bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length);
  }
  while(myNetworkStream.DataAvailable);
}

Обратите внимание, что я только пытаюсь проиллюстрировать идею, и вы можете подумать о возврате Task<TResult>, а также о том, следует ли использовать цикл do{}while, какую-либо дополнительную обработку или очистку и т. д. - все в соответствии с вашими потребностями.

Я также хотел бы обратить ваше внимание на статью Стивена Туба Как отменить неотменяемые асинхронные операции? и расширение WithCancellation, которое он там создает.

person G. Stoynev    schedule 07.03.2013
comment
Я забыл о проверке DataAvailable! Спасибо за пример кода и за ссылку на статью Стивена Туба. Оба будут полезны для меня, чтобы достичь того, что я после. Спасибо! - person Swampie; 08.03.2013

Вы не можете отменить ReadAsync, так как внутренний вызов неуправляемый и использует порты IOCompletion. Возможны следующие варианты.

  1. Используйте Socket.Shutdown(). Это вернет ReadAsync с ошибкой сокета OperationAborted.
  2. Дождитесь тайм-аута чтения.
  3. Перед чтением из сокета проверьте, доступны ли данные.
person James Harmon    schedule 07.03.2013
comment
К сожалению, я не использую Sockets, поэтому не думаю, что смогу использовать Socket.Shutdown. Я создаю BluetoothClient (из 32Feet.NET) и вызываю BluetoothClient.Connect(). После подключения я получаю поток через BluetoothClient.GetStream(). Что касается тайм-аута, MSDN говорит, что тайм-аут Stream применяется только для синхронного Read() - подтверждается установкой свойства ReadTimeout, а ReadAsync() никогда не истекает. Тем не менее, немного о проверке данных, доступных из сокета, полезно, и я совершенно забыл об этом. Спасибо - person Swampie; 08.03.2013
comment
Мое понимание прочитанного подвело меня. Я рад слышать, что мой несвязанный ответ все же помог. - person James Harmon; 08.03.2013
comment
При использовании TCPClient метод TCPClient.Close() можно использовать для создания исключения при зависшем вызове readAsync(). - person unintentionally left blank; 28.11.2017

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

try {

 ....

 Task<int> readTask = input.ReadAsync(buffer, 0, buffer.Length);
 readTask.Wait(myCancellationToken);
 int readBytes= await readTask;

....

}
catch ( OperationCanceledException e )
{
   // handle cancellation
}
person Maurice Amsellem    schedule 11.08.2016
comment
Вы используете это с NetworkStream ReadAsync и WriteAsync? Можете ли вы привести хороший рабочий пример для обоих? Спасибо - person cd491415; 26.02.2019
comment
Вызов Wait блокирует поток, лишая преимущества использования ReadAsync. - person Joakim M. H.; 25.09.2019