go - 如何正确使用同步机制从串口读取输入

标签 go serial-port goroutine

我目前正在使用两个 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 在异步 worker 完成工作之前不会退出。

但是正如您正确地注意到的那样,目前没有强制执行 goroutines 执行顺序的方法。您可以使用基于“ token ”的 channel ;想象一下传球,你只有在持球时才能工作。

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 继续,这意味着您正在对并发程序结构实现“串行”约束。

相反,您可以通过删除 gosync.WaitGroup 来删除并发性,然后按顺序调用函数,因为这就是您试图实现的目标。

关于go - 如何正确使用同步机制从串口读取输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50618246/

相关文章:

javascript - 从/到 Javascript 字符串从 Go int64/uint64 序列化反序列化

Go 项目结构 - 最佳实践

go - Go 中来自 GCF 的日志不包含日志级别

python - 行缓冲串行输入

python - 在 Windows 上使用 pyserial 将串行数据写入 Arduino

go - 奇怪的选择案例默认行为

go - 这是因为go编译器优化了代码吗?

go - 如何在golang中比较两个版本号字符串

Java:如何将传入的数据片段收集到正确终止的字符串中以供后续解析?

go - 将上下文从 gRPC 端点传递到 goroutine 收到上下文取消错误