SerialPort.DataReceived事件多线程调用时是否需使用lock?
关于SerialPort DataReceived事件中List操作的线程安全问题
问题描述
在如下代码中,SerialPort_obj_DataReceived作为串口数据接收的事件处理方法,执行dataReceived.AddRange(buff)时是否需要加lock保护?
public partial class Ctwing : Form { object myLock = new object(); SerialPort serialPort_obj; List<byte> dataReceived; public Ctwing() { InitializeComponent(); dataReceived = new List<byte>(); serialPort_obj = new SerialPort(); serialPort_obj.DataReceived += SerialPort_obj_DataReceived; } private void SerialPort_obj_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; byte[] buff = new byte[sp.BytesToRead]; sp.Read(buff, 0, sp.BytesToRead); dataReceived.AddRange(buff); /* should I to use a lock or not ? lock(myLock){ dataReceived.AddRange(buff); } */ } }
回答
必须加lock保护,原因如下:
- 串口的
DataReceived事件由.NET IO线程池中的线程触发,并非单线程执行。当串口数据大量涌入时,可能会有多个线程同时进入事件处理方法,对dataReceived进行修改。 List<T>是非线程安全的集合,AddRange操作内部涉及多个非原子步骤:比如检查内部数组容量、复制数据、更新元素计数等。多线程同时操作会破坏集合的内部状态,导致数据错乱、索引越界异常,甚至程序崩溃。
正确的写法应该将AddRange操作放入lock块中,确保同一时间只有一个线程能修改dataReceived:
private void SerialPort_obj_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort sp = (SerialPort)sender; byte[] buff = new byte[sp.BytesToRead]; sp.Read(buff, 0, sp.BytesToRead); lock(myLock) { dataReceived.AddRange(buff); } }
另外需要注意:如果其他线程(比如UI线程)需要读取dataReceived的内容,读取操作也必须使用同一个myLock进行保护,避免出现读写竞争的线程安全问题。
内容的提问来源于stack exchange,提问作者Free HK
相关产品推荐
相关产品推荐

