如何正确使用同步机制从串口读取输入
How to correctly use synchronization mechanisms to read input from serial port
我目前正在使用两个 goroutines 从带有串行端口的 Arduino 读取输入。
这些例程的目的是让第一个例程可以读取输入并输出数据,然后第二个例程执行相同的操作。哪个例程何时执行并不重要,我希望一个 goroutine 在另一个 goroutine 完成执行后执行。这是我的代码:
package main
import (
"log"
"github.com/tarm/serial"
"bufio"
"sync"
"fmt"
)
func readFirstLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup){
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func readSecondLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup){
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func main() {
usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: 0}
port, err := serial.OpenPort(usbRead)
var wg sync.WaitGroup
wg.Add(2)
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(port)
for {
go readFirstLine(scanner, port, &wg)
go readSecondLine(scanner, port, &wg)
wg.Wait()
}
}
这是预期的输出(因为只有 三 行数据,我希望每个 goroutine 每次读取和输出相同的三行 - 这将是循环):
{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
{"blink":["true"],"actuator":"arduinoLED"}
{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
{"blink":["true"],"actuator":"arduinoLED"}
然而,这是实际输出:
,46.[25.41545,25.41545,25.41545],"sensor":"LM35DZ"}
{"blink":["true","true","true"],"actuato ":"arduinoLED"}
{"tempertemperature":[25.41545,25.41545,25.41545],"sensor":"LM35DZ"}
{"blink":["true","true","true"],"actuator":"arduinoLED"}
{"tempert"eep uratr:" [hum.7],"t idiy[: "se],n :"Dr"H }
2"{ perema ":[re2 54541] nsoser LM3"5
如您所见,数据存在冲突。
我的问题是:我知道你必须使用某种同步机制来确保在第一个 goroutine 完成执行后执行一个 go routine。我是这种语言的新手(并且从串行端口读取)所以我不太确定我是否正确使用 sync.WaitGroup 来达到预期的结果。我很想知道如何解决这个问题,或者我是否应该使用不同的同步机制以及如何正确使用它。
此处使用的 sync.WaitGroup
将确保您的主 goroutine 在异步工作者完成工作之前不会退出。
但是正如您正确地注意到的那样,目前没有强制执行 goroutines 执行顺序的方法。您可以使用基于 "token" 的通道;想象一下传球,只有持球才能工作
func readFirstLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup, ch chan<- struct{}){
defer wg.Done()
defer func () {ch <-struct {}{}}() // write a token to the channel when done
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func readSecondLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup, ch <- chan struct{}){
<-ch // don't start work until the notification / token is received
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func main() {
// trimmed ...
ch := make(chan struct{})
for {
go readFirstLine(scanner, port, &wg, ch)
go readSecondLine(scanner, port, &wg, ch)
wg.Wait()
}
}
但是:正如评论中所指出的,在 goroutine1 完成执行之前,您不允许 goroutine2 继续,这意味着您正在对 "serial" 强制执行约束并发程序结构。
相反,您可以通过删除 go
和 sync.WaitGroup
来删除并发性,然后按顺序调用函数,因为这似乎是您要实现的目标。
我目前正在使用两个 goroutines 从带有串行端口的 Arduino 读取输入。
这些例程的目的是让第一个例程可以读取输入并输出数据,然后第二个例程执行相同的操作。哪个例程何时执行并不重要,我希望一个 goroutine 在另一个 goroutine 完成执行后执行。这是我的代码:
package main
import (
"log"
"github.com/tarm/serial"
"bufio"
"sync"
"fmt"
)
func readFirstLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup){
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func readSecondLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup){
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func main() {
usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: 0}
port, err := serial.OpenPort(usbRead)
var wg sync.WaitGroup
wg.Add(2)
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(port)
for {
go readFirstLine(scanner, port, &wg)
go readSecondLine(scanner, port, &wg)
wg.Wait()
}
}
这是预期的输出(因为只有 三 行数据,我希望每个 goroutine 每次读取和输出相同的三行 - 这将是循环):
{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
{"blink":["true"],"actuator":"arduinoLED"}
{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
{"blink":["true"],"actuator":"arduinoLED"}
然而,这是实际输出:
,46.[25.41545,25.41545,25.41545],"sensor":"LM35DZ"}
{"blink":["true","true","true"],"actuato ":"arduinoLED"}
{"tempertemperature":[25.41545,25.41545,25.41545],"sensor":"LM35DZ"}
{"blink":["true","true","true"],"actuator":"arduinoLED"}
{"tempert"eep uratr:" [hum.7],"t idiy[: "se],n :"Dr"H }
2"{ perema ":[re2 54541] nsoser LM3"5
如您所见,数据存在冲突。
我的问题是:我知道你必须使用某种同步机制来确保在第一个 goroutine 完成执行后执行一个 go routine。我是这种语言的新手(并且从串行端口读取)所以我不太确定我是否正确使用 sync.WaitGroup 来达到预期的结果。我很想知道如何解决这个问题,或者我是否应该使用不同的同步机制以及如何正确使用它。
此处使用的 sync.WaitGroup
将确保您的主 goroutine 在异步工作者完成工作之前不会退出。
但是正如您正确地注意到的那样,目前没有强制执行 goroutines 执行顺序的方法。您可以使用基于 "token" 的通道;想象一下传球,只有持球才能工作
func readFirstLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup, ch chan<- struct{}){
defer wg.Done()
defer func () {ch <-struct {}{}}() // write a token to the channel when done
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func readSecondLine(scanner *bufio.Scanner, port *serial.Port, wg *sync.WaitGroup, ch <- chan struct{}){
<-ch // don't start work until the notification / token is received
defer wg.Done()
for scanner.Scan() {
fmt.Println("\n", scanner.Text())
}
}
func main() {
// trimmed ...
ch := make(chan struct{})
for {
go readFirstLine(scanner, port, &wg, ch)
go readSecondLine(scanner, port, &wg, ch)
wg.Wait()
}
}
但是:正如评论中所指出的,在 goroutine1 完成执行之前,您不允许 goroutine2 继续,这意味着您正在对 "serial" 强制执行约束并发程序结构。
相反,您可以通过删除 go
和 sync.WaitGroup
来删除并发性,然后按顺序调用函数,因为这似乎是您要实现的目标。