Stop Stream.BeginRead () - PullRequest
       34

Stop Stream.BeginRead ()

4 голосов
/ 20 июля 2011

Мне нужно прочитать данные с моего виртуального COM-порта и обнаружить сообщение «Dreq».Как только я нажимаю кнопку подключения, он подключается к моему порту COM8 и начинает читать в новой теме.У меня также есть кнопка отключения, в которой я хочу закрыть чтение и отключиться от порта COM8.Однако у меня проблемы с закрытием BeginRead.

public partial class Form1 : Form
{
    SerialPort sp;
    Stream stream;
    IAsyncResult recv_result;

    private void button1_Click(object sender, EventArgs e)
    {
        sp = new SerialPort("COM8", 9600);
        sp.Open();
        sp.ReadTimeout = 50000;
        sp.NewLine = "\n\r\0";
        stream = sp.BaseStream;
        recv_result = stream.BeginRead(new byte[1], 0, 0, new 
                                       AsyncCallback(ReadCallBack), stream);
    }

    private void ReadCallBack(IAsyncResult ar)
    {            
        Stream stream = (Stream)ar.AsyncState;
        string temp;

        while (stream.CanRead)
        {
            temp = sp.ReadLine();                
            // ... do something with temp
        }
    }

    private void disconnectButton_Click(object sender, EventArgs e)
    {
        stream.EndRead(recv_result);
        sp.Close();
    }
}

Ответы [ 3 ]

2 голосов
/ 13 февраля 2012

Вы можете попробовать эту программу.

using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.IO;
using System.Text;

public class clsState {
    private const int BUFFER_SIZE = 1024;
    public byte[] BytesBuffer = new byte[BUFFER_SIZE];
    public SerialPort sp;
    public Stream stream;
}

public partial class Form1 : Form {

    SerialPort sp;
    Stream stream;
    IAsyncResult recv_result;

    bool endLoop = false;
    Thread _readThread;
    private ManualResetEvent _readDone = new ManualResetEvent(false);

    private void button1_Click(object sender, EventArgs e) {
        sp = new SerialPort("COM8", 9600);
        sp.Open();
        sp.ReadTimeout = 50000;
        sp.NewLine = "\n\r\0";
        stream = sp.BaseStream;

        // save serial port and stream to state object
        clsState state =  new clsState();
        state.sp = sp;
        state.stream = stream;

        // create worker thread
        endLoop = false;
        _readThread = new Thread(() => ReadThreadProc(state))
        _readThread.IsBackground = true;
        _readThread.Start();
    }

    private void ReadThreadProc(clsState state) {
        while (endLoop == false){
            // open and then close the gate as soon as after one thread passed
            _readDone.Reset();

            // starting ascynchronous read 
            recv_result = state.stream.BeginRead(state.BytesBuffer, 0, state.BytesBuffer.Length, new AsyncCallback(ReadCallBack), state.stream);

            // worker thread block in here (waiting for... _readDone.Set())
            _readDone.WaitOne();
        }
    }

    private void ReadCallBack(IAsyncResult ar) {   
        string temp;
        int bytesRead = 0;

        // read serial port and stream from IAsyncResult
        clsState state = (clsState) ar.AsyncState;

        // if port serial is open and..
        if (state.sp.IsOpen) {
            // the stream can read then..
            if(state.stream.CanRead) {
                // wait for asynchronous read to completed
                bytesRead = state.stream.EndRead(ar);
            }
        }

        if(bytesRead > 0) {
           // convert data in state.BytesBuffer from bytes array to string and save to temp variable
           temp = Encoding.ASCII.GetString(state.BytesBuffer);
            // open gate for next data
           _readDone.Set();
        }
    }

    private void disconnectButton_Click(object sender, EventArgs e) {
        // ending loop and will kill worker thread...
        endLoop = true;

        // release begin read
        _readDone.Set();      

        if (_readThread != null){
            if (_readThread.IsAlive){ // if worker thread still live
                _readThread.Join();   // wait thread off in here..
            }
        }

        // close serial port
        if (sp.IsOpen) sp.Close();

        // close stream and dispose it 
        if (stream.CanRead || stream.CanWrite) {
            stream.Close();
            stream.Dispose();
        }
    }
}
2 голосов
/ 20 июля 2011

Вы можете попробовать позвонить sp.DiscardOutBuffer(). Он вызовет ваш обратный вызов для чтения, и вы сможете использовать stream.EndRead().

private void disconnectButton_Click(object sender, EventArgs e)
{
    sp.DiscardOutBuffer();
    stream.EndRead(recv_result);
    sp.Close();
}
0 голосов
/ 27 февраля 2018

Это старый пост, но я думаю, что это решение может помочь людям с той же проблемой.

Я использовал устаревший код для приложения и обнаружил, что проблема с BeginRead и EndRead заключается в том, что невозможно отменить асинхронную операцию. Поэтому, когда вы закрываете порт, ваш вызов BeginRead остается там до тех пор, пока в порту не будет получен другой байт, тогда ваш вызов EndRead освободит порт. Если этого не происходит, приложение может зависнуть, и даже диспетчер задач не сможет его закрыть, пока вы не отключите кабель последовательного порта!

К счастью, библиотека TPL может решить эту проблему очень простым и элегантным способом. CancelToken - это то, что вам нужно:

В открытом порту:

while (x)   
   var myTask = _serialPort.BaseStream.ReadAsync(_serialBuffer, 0, _bufferLength, cancelToken.Token);
   var bytesRead = await myTask;
   ... your business logic here... 
    if ((myTask.IsCanceled) || (myTask.IsFaulted)) break;
}

При закрытии порта:

cancelToken.Cancel(false);

Обратите внимание, что цикл while лучше рекурсивного вызова, потому что когда порт передает много информации, исключение переполнения стека выдается через 15 минут.

Это очень простая реализация. Я надеюсь, что это поможет

...