一个简单的读者写者的程序遇到的问题

我大概想写一个按行读取文件送到channel里,另一个从channel中取出处理的程序。遇到了一些问题:

  1. reader按行读取,等读完了用ok <- nil通知,这里我就觉得很不优雅,两个channel都是作为参数传进来的,没有用上返回值。我不知道怎么修改。
  2. 输出还没输出完,程序就结束了。但是从len(ch)又可以看到ch是消费完了的。怎么办?
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    err := program()
    if err != nil {
        panic(err)
    }
}

func program() error {
    f, err := os.Open("data.txt")
    if err != nil {
        return err
    }
    defer f.Close()

    scanner := bufio.NewScanner(f)
    ch := make(chan string, 3)
    ok := make(chan any)
    ok2 := make(chan any)

    go reader(scanner, ch, ok)
    go outputer(ch, ok2)

    <-ok
    if err := scanner.Err(); err != nil {
        return err
    }
    close(ch)
    <-ok2
    fmt.Println(len(ch))
    fmt.Println("完成")

    return nil
}

func reader(scanner *bufio.Scanner, data chan string, ok chan any) {
    for scanner.Scan() {
        data <- scanner.Text()
    }
    ok <- nil
}

func outputer(data chan string, ok chan any) {
    for s := range data {
        go func(s string) {
            fmt.Println(s)
        }(s)
    }
    ok <- nil
}

3.没办法调试。我用的是VSCode,会报错invalid command

img

4.程序的所有方面的结构,如果各位有改进建议,还请不吝赐教。

1.该加sleep加sleep,执行太快你根本分辨不出来谁先执行谁后执行的,该加同步锁加同步锁,避免线程不安全
2.打印了完成并不是真的完成,因为子线程没有完,但主线程已经执行完了,此时先打印了完成,但子线程还在执行
你可以在启动子线程之后执行线程同步,必须线程执行完毕再继续执行主线程
也可以在子线程里判断数据全都处理完了再打印完成,而不是启动了线程就直接打印完成
3.多线程是没办法断点跟踪的,这在哪个语言都一样,你需要一些特别的调试技巧

1、使用布尔类型的信号量通知读取完毕,代替"ok <- nil"的方式:

func reader(scanner *bufio.Scanner, data chan string, done chan bool) {
    for scanner.Scan() {
        data <- scanner.Text()
    }
    close(data)
    done <- true
}

func outputer(data chan string, done chan bool) {
    for s := range data {
        fmt.Println(s)
    }
    done <- true
}

func program() error {
    f, err := os.Open("data.txt")
    if err != nil {
        return err
    }
    defer f.Close()
 
    scanner := bufio.NewScanner(f)
    ch := make(chan string, 3)
    done1 := make(chan bool)
    done2 := make(chan bool)
 
    go reader(scanner, ch, done1)
    go outputer(ch, done2)
 
    <-done1
    if err := scanner.Err(); err != nil {
        return err
    }
    <-done2
    fmt.Println("完成")
 
    return nil
}

2、修改"outputer"中的匿名函数为普通函数,提高代码可读性

func processLine(s string) {
    fmt.Println(s)
}

func outputer(data chan string, done chan bool) {
    for s := range data {
        processLine(s)
    }
    done <- true
}

3、将VSCode的调试设置按照文档指示进行设置

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^