使用Go多线程读取Arduino串口出现乱码的问题求助
多线程读取Arduino串口出现乱码的问题分析与解决
你的问题核心不是Arduino无输出时的读取,而是多个goroutine同时操作同一个bufio.Scanner导致的线程安全问题——bufio.Scanner本身并不是线程安全的,多个协程同时调用Scan()会打乱它的内部缓冲区状态,直接导致数据解析混乱、出现乱码。
先看你的原代码:
func readFirstLine(scanner *bufio.Scanner, port *serial.Port){ for scanner.Scan() { log.Printf("%q\n", scanner.Text()) } time.Sleep(time.Duration(1)*time.Second) } func readSecondLine(scanner *bufio.Scanner, port *serial.Port){ time.Sleep(time.Duration(1)*time.Second) for scanner.Scan() { log.Printf("%q\n", scanner.Text()) } } func main() { usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: 0} port, err := serial.OpenPort(usbRead) if err != nil { log.Fatal(err) } scanner := bufio.NewScanner(port) for true { go readFirstLine(scanner, port) go readSecondLine(scanner, port) time.Sleep(time.Duration(2)*time.Second) } }
你预期的输出是完整的JSON格式数据:
{"temperature":[26,26],"humidity":[54.2,54.2],"sensor":"DHT22"} {"temperature":[23.46041,23.46041],"sensor":"LM35DZ"} {"blink":["true","true"],"actuator":"arduinoLED"} {"temperature":[26,26],"humidity":[54.2,54.2],"sensor":"DHT22"} {"temperature":[23.46041,23.46041],"sensor":"LM35DZ"} {"blink":["true","true"],"actuator":"arduinoLED"}
但实际出现乱码,本质是多个协程同时抢占scanner的读取权限,导致缓冲区数据被拆分、重叠读取。
解决方法
正确的做法是用单个goroutine负责串口的读取工作,然后将读取到的完整行数据通过channel分发给其他需要处理的协程,这样既保证了串口读取的线程安全,也能实现多线程处理数据的需求。
修改后的代码示例:
import ( "bufio" "log" "time" "github.com/tarm/serial" ) // 处理读取到的串口数据的协程1 func processLine1(lineChan <-chan string) { for line := range lineChan { log.Printf("Processor 1 received: %q\n", line) } } // 处理读取到的串口数据的协程2 func processLine2(lineChan <-chan string) { for line := range lineChan { log.Printf("Processor 2 received: %q\n", line) } } // 单独的串口读取协程 func readSerial(port *serial.Port, lineChan chan<- string) { defer close(lineChan) scanner := bufio.NewScanner(port) // 如果你的JSON行可能超过默认缓冲区大小,可以调整缓冲区 // buf := make([]byte, 1024) // scanner.Buffer(buf, bufio.MaxScanTokenSize) for scanner.Scan() { line := scanner.Text() lineChan <- line } if err := scanner.Err(); err != nil { log.Fatalf("Serial read error: %v", err) } } func main() { usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: time.Second * 1} // 设置合理的读取超时 port, err := serial.OpenPort(usbRead) if err != nil { log.Fatal(err) } defer port.Close() lineChan := make(chan string, 10) // 带缓冲的channel,避免阻塞读取 // 启动处理协程 go processLine1(lineChan) go processLine2(lineChan) // 启动串口读取协程 readSerial(port, lineChan) }
关键说明
- 线程安全保障:单个协程负责串口读取,避免多个协程操作同一个
bufio.Scanner,从根源上解决竞态条件导致的乱码。 - 数据分发:通过channel将完整的行数据传递给处理协程,保证每个处理协程拿到的都是完整、正确的JSON数据。
- 超时设置:给串口配置合理的
ReadTimeout,避免读取协程无限阻塞在无数据的状态。 - 缓冲区调整:如果你的JSON数据行较长,可以调整
scanner.Buffer()的大小,避免因缓冲区不足导致的截断问题。
内容的提问来源于stack exchange,提问作者Melissa
相关产品推荐
相关产品推荐

