如何在多个不同的控制台输出 goroutines?
How to make goroutines output in multiple different consoles?
我正在练习使用 goroutines,发现如果两个 goroutines 同时打印,会变得难以阅读。
func main() {
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
wg := &sync.WaitGroup{}
t1 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T1 : ", i)
}
wg.Done()
}
t2 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T2 : ", i)
}
wg.Done()
}
wg.Add(2)
go t1(wg)
go t2(wg)
wg.Wait()
}
输出:
T1 : 0
T2 : 0
T2 : 1
T1 : 1
T1 : 2
T2 : 2
T1 : 3
T2 : 3
T1 : 4
T2 : 4
T1 : 5
T2 : 5
T1 : 6
T2 : 6
T2 : 7
T1 : 7
T2 : 8
T1 : 8
T1 : 9
T2 : 9
T2 : 10
T1 : 10
......
有什么办法可以打开多个控制台,让两个goroutine在不同的控制台输出?
- 你可以使用一个简单的TCP终端服务器-首先运行这个TCP终端服务器,你不需要关闭它,只要你需要(或只需使用 Netcat 命令:
nc -l 8080
然后转到 #2 并写入此 TCP 连接,例如 "127.0.0.1:8080"):
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
w1, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, w1)
w1.Close()
}
}
- 然后将此添加到您的代码中:
w1, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer w1.Close()
- 然后在代码中使用
w1
作为另一个 io.Writer
,例如fmt.Fprintln(w1, "T1 : ", i)
,例子:
package main
import (
"fmt"
"log"
"math/rand"
"net"
"sync"
"time"
)
func main() {
w1, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer w1.Close()
// your code:
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
wg := &sync.WaitGroup{}
t1 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Fprintln(w1, "T1 : ", i)
}
wg.Done()
}
t2 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T2 : ", i)
}
wg.Done()
}
wg.Add(2)
go t1(wg)
go t2(wg)
wg.Wait()
}
fmt.Print*(...)
函数是 fmt.Fprint*(os.Stdout, ...)
的包装器。如果你想让两个 goroutines 写入不同的作者,你的代码中的第一步是提供他们应该使用的作者:
t1 := func(wg *sync.WaitGroup, out io.Writer) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Fprintln(out, "T1 : ", i)
}
wg.Done()
}
...
// for starters : keep using the same os.Stdout writer
go t1(wg, os.Stdout)
而不是原始的 io.Writer
,您可能想要传递一些您可以登录的东西;标准库有 log.Logger
结构:
t1 := func(wg *sync.WaitGroup, l *log.Logger) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
l.Println(i)
}
wg.Done()
}
...
// all logging libraries will allow you to specify some option which you can tune :
l1 := log.New(os.Stdout, "T1 : ", log.Ltime) // will prefix all messages with "[time] T1 : "
go t1(wg, l1)
// this allows you to write :
l2 := log.New(os.Stdout, "T2 : ", log.Ltime) // will prefix all messages with "[time] T2 : "
go t2(wg, l2)
您可以查看 golang 日志库以了解更多功能——不过,不要花太多时间为快速一次性项目选择日志框架。
现在您已经有了明确的方法将两个不同的编写器(或记录器)传递给您的 goroutines,选择适合您需要写入的内容:
- 让 goroutine 1 记录到 Stdout,让 goroutine 2 记录到 Stderr
- 登录到
file1
和 file2
——只需 运行 tail -f file2
在一些单独的终端中获得输出的“实时视图”
io.Discard
是 /dev/null
的 golang 内置等价物
- 记录到命名管道、unix 套接字、tcp 连接...
我正在练习使用 goroutines,发现如果两个 goroutines 同时打印,会变得难以阅读。
func main() {
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
wg := &sync.WaitGroup{}
t1 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T1 : ", i)
}
wg.Done()
}
t2 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T2 : ", i)
}
wg.Done()
}
wg.Add(2)
go t1(wg)
go t2(wg)
wg.Wait()
}
输出:
T1 : 0
T2 : 0
T2 : 1
T1 : 1
T1 : 2
T2 : 2
T1 : 3
T2 : 3
T1 : 4
T2 : 4
T1 : 5
T2 : 5
T1 : 6
T2 : 6
T2 : 7
T1 : 7
T2 : 8
T1 : 8
T1 : 9
T2 : 9
T2 : 10
T1 : 10
......
有什么办法可以打开多个控制台,让两个goroutine在不同的控制台输出?
- 你可以使用一个简单的TCP终端服务器-首先运行这个TCP终端服务器,你不需要关闭它,只要你需要(或只需使用 Netcat 命令:
nc -l 8080
然后转到 #2 并写入此 TCP 连接,例如 "127.0.0.1:8080"):
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
w1, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, w1)
w1.Close()
}
}
- 然后将此添加到您的代码中:
w1, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer w1.Close()
- 然后在代码中使用
w1
作为另一个io.Writer
,例如fmt.Fprintln(w1, "T1 : ", i)
,例子:
package main
import (
"fmt"
"log"
"math/rand"
"net"
"sync"
"time"
)
func main() {
w1, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer w1.Close()
// your code:
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
wg := &sync.WaitGroup{}
t1 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Fprintln(w1, "T1 : ", i)
}
wg.Done()
}
t2 := func(wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Println("T2 : ", i)
}
wg.Done()
}
wg.Add(2)
go t1(wg)
go t2(wg)
wg.Wait()
}
fmt.Print*(...)
函数是 fmt.Fprint*(os.Stdout, ...)
的包装器。如果你想让两个 goroutines 写入不同的作者,你的代码中的第一步是提供他们应该使用的作者:
t1 := func(wg *sync.WaitGroup, out io.Writer) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
fmt.Fprintln(out, "T1 : ", i)
}
wg.Done()
}
...
// for starters : keep using the same os.Stdout writer
go t1(wg, os.Stdout)
而不是原始的 io.Writer
,您可能想要传递一些您可以登录的东西;标准库有 log.Logger
结构:
t1 := func(wg *sync.WaitGroup, l *log.Logger) {
for i := 0; i < 100; i++ {
time.Sleep(time.Microsecond * time.Duration(r1.Intn(100)))
l.Println(i)
}
wg.Done()
}
...
// all logging libraries will allow you to specify some option which you can tune :
l1 := log.New(os.Stdout, "T1 : ", log.Ltime) // will prefix all messages with "[time] T1 : "
go t1(wg, l1)
// this allows you to write :
l2 := log.New(os.Stdout, "T2 : ", log.Ltime) // will prefix all messages with "[time] T2 : "
go t2(wg, l2)
您可以查看 golang 日志库以了解更多功能——不过,不要花太多时间为快速一次性项目选择日志框架。
现在您已经有了明确的方法将两个不同的编写器(或记录器)传递给您的 goroutines,选择适合您需要写入的内容:
- 让 goroutine 1 记录到 Stdout,让 goroutine 2 记录到 Stderr
- 登录到
file1
和file2
——只需 运行tail -f file2
在一些单独的终端中获得输出的“实时视图” io.Discard
是/dev/null
的 golang 内置等价物
- 记录到命名管道、unix 套接字、tcp 连接...