想要使用通道将从os.Stdin读取的内容写入os.Stdout。

huangapple go评论79阅读模式
英文:

Want to write from os.Stdin to os.Stdout using channels

问题

/* 想要使用通道从os.Stdin写入os.Stdout(在下面的代码中使用fmt.Println()) */
package main
import (
"fmt"
"io"
"os"
"bufio"
)

type message []byte
/* 运行用于读取stdin的goroutine的函数 */
func read(r io.Reader) <-chan message {
lines := make(chan message)
go func() {
defer close(lines)
scan := bufio.NewScanner(r)
for scan.Scan() {
lines <- message(scan.Bytes())
}
}()
return lines
}

func main() {
mes := make(chan message, 1)
sig := make(chan bool)
ch := read(os.Stdin) //从Stdin读取
for {
select {
case anu := <-mes:
fmt.Println("Message to stdout")
fmt.Println(string(anu)) //写入Stdout
case mes <- <-ch:
fmt.Println("Message to channel 2")
continue
}
}
<-sig
}

/*
输出为:

go run writetochan.go
Golang
Message to channel 2
Fun <<< 延迟输出,意味着只有在放入另一条消息后我们才会得到第一条消息
Message to stdout
Golang

期望输出:
go run writetochan.go
Golang
Message to channel 2
Message to stdout
Golang
Fun
Message to channel 2

*/

希望实现上面显示的输出。

我们正在从一个通道写入,该通道从用户读取所有的stdin,然后写入stdout。通道读取在goroutine中进行。形成了一个虚拟通道(sig),以便我们可以无限期地运行它(仅供现在使用)。

英文:
/* Want to write from os.Stdin to os.Stdout(fmt.Println() in below code) using channels*/
package main
import (
    &quot;fmt&quot;
    &quot;io&quot;
    &quot;os&quot;
    &quot;bufio&quot;
)

type message []byte
/* Function to run the groutine to run for stdin read */
func read (r io.Reader) &lt;-chan message{
    lines := make (chan message)
    go func() {
        defer close(lines)
        scan := bufio.NewScanner(r)
        for scan.Scan() {
            lines &lt;- message(scan.Bytes())
        }   
    }() 
    return lines
}


func main() {
    mes := make (chan message, 1)
    sig := make (chan bool)
    ch := read (os.Stdin) //Reading from Stdin
    for {
        select {
            case anu := &lt;-mes:
                fmt.Println(&quot;Message to stdout&quot;)
                fmt.Println(string(anu)) //Writing to Stdout
            case mes &lt;- &lt;-ch:
                fmt.Println(&quot;Message to channel 2&quot;)
                continue
        }   
    }   
    &lt;-sig

/*
The O/P is :

go run writetochan.go 
Golang
Message to channel 2
Fun     &lt;&lt;&lt; Delayed O/P golang means after putting one more 
            message only we are getting First message
Message to stdout
Golang


Expected O/P:
go run writetochan.go 
Golang
Message to channel 2
Message to stdout
Golang
Fun
Message to channel 2

*/
}

Want to achieve the O/P shown above.

We are writing from one channel which reads all the stdin from the user and then writes to the stdout. Channel read is happening in goroutine. A dummy channel is formed (sig) so that we can run it indefinitely (Just for now).

答案1

得分: 3

问题是你在第二个select语句中有两个通道操作。select只能防止_外部_操作阻塞。因此,<-ch调用会立即执行,并且没有select的阻塞保护,因此整个select语句会阻塞,直到在该通道上接收到其他内容(这需要另一个输入,以便read()可以再次在该通道上发送)。

不幸的是,修复方法不够简洁。如果你将其更改为case m := <-ch:,那么将m发送到mes将阻塞select,如果它已经在缓冲区中,则可能导致死锁。可能最简单的修复方法是只使用一个通道,而不是两个。示例:

package main

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

/* Function to run the groutine to run for stdin read */
func read(r io.Reader) <-chan string {
	lines := make(chan string)
	go func() {
		defer close(lines)
		scan := bufio.NewScanner(r)
		for scan.Scan() {
			lines <- scan.Text()
		}
	}()
	return lines
}

func main() {
	mes := read(os.Stdin) //Reading from Stdin
	for anu := range mes {
		fmt.Println("Message to stdout")
		fmt.Println(anu) //Writing to Stdout
	}
}

请注意,我将你的scan.Bytes()调用更改为scan.Text(),因为scan.Bytes()的注释明确指出,它返回的切片的底层数组不安全,可能会被后续的Scan调用覆盖。

另一种选择是使用单独的goroutine在通道之间传递消息:

package main

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

/* Function to run the groutine to run for stdin read */
func read(r io.Reader) <-chan string {
	lines := make(chan string)
	go func() {
		defer close(lines)
		scan := bufio.NewScanner(r)
		for scan.Scan() {
			s := scan.Text()
			lines <- s
		}
	}()
	return lines
}

func main() {
	mes := make(chan string, 1)
	ch := read(os.Stdin) //Reading from Stdin
	go func() {
		for m := range ch {
			fmt.Println("Message to channel 2")
			mes <- m
		}
	}()
	for anu := range mes {
		fmt.Println("Message to stdout")
		fmt.Println(anu) //Writing to Stdout
	}
}
英文:

The problem is that you have two channel operations in your second select case. Select only prevents the outer operation from blocking. Thus the &lt;-ch call is evaluated immediately, and doesn't have the blocking protection of the select, so the entire select statement blocks until something else is received on that channel (which requires another input so read() can send again on that channel).

Unfortunately, the fix isn't as clean. If you change it to case m := &lt;-ch:, then sending m over mes will block the select, and can result in a deadlock if it's already at buffer. Probably the easiest way to fix it is to only have a single channel, rather than two. Example:

package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;os&quot;
)

/* Function to run the groutine to run for stdin read */
func read(r io.Reader) &lt;-chan string {
	lines := make(chan string)
	go func() {
		defer close(lines)
		scan := bufio.NewScanner(r)
		for scan.Scan() {
			lines &lt;- scan.Text()
		}
	}()
	return lines
}

func main() {
	mes := read(os.Stdin) //Reading from Stdin
	for anu := range mes {
		fmt.Println(&quot;Message to stdout&quot;)
		fmt.Println(anu) //Writing to Stdout
	}
}

Note that I changed your scan.Bytes() call to scan.Text(), because the comments for scan.Bytes() specifically state that the underlying array of the slice it returns is not safe against being overwritten by following Scan calls.

Another alternative is to use a separate goroutine to translate the messages between the channels:

package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;os&quot;
)

/* Function to run the groutine to run for stdin read */
func read(r io.Reader) &lt;-chan string {
	lines := make(chan string)
	go func() {
		defer close(lines)
		scan := bufio.NewScanner(r)
		for scan.Scan() {
			s := scan.Text()
			lines &lt;- s
		}
	}()
	return lines
}

func main() {
	mes := make(chan string, 1)
	ch := read(os.Stdin) //Reading from Stdin
	go func() {
		for m := range ch {
			fmt.Println(&quot;Message to channel 2&quot;)
			mes &lt;- m
		}
	}()
	for anu := range mes {
		fmt.Println(&quot;Message to stdout&quot;)
		fmt.Println(anu) //Writing to Stdout
	}
}

huangapple
  • 本文由 发表于 2017年4月25日 20:26:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/43610646.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定