停止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(); } } 

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

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

你可以试试这个程序。

 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(); } } } 

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

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

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