停止Stream.BeginRead()

时间:2011-07-20 09:44:52

标签: c# stream beginread

我需要从虚拟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 个答案:

答案 0 :(得分:2)

您可以尝试拨打sp.DiscardOutBuffer()。它将调用您的读回调,然后您可以使用stream.EndRead()

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

答案 1 :(得分:2)

你可以尝试这个程序。

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 :(得分:0)

这是一个老帖子,但我认为这个解决方案可以帮助有同样问题的人。

我正在为应用程序使用遗留代码,我发现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分钟后会抛出堆栈溢出异常。

这是一个非常简单的实现。我希望它有所帮助