通过这段 Golang 代码,有点疑问

2021-12-24 17:55:38 +08:00
 gy123

以下代码可以做到所有协程睡眠 3 秒后直接输出;

楼主是写 java 的,在想 java 要怎么能实现 go 通过协程实现的这个例子呢? (1)直接使用 100000 个线程执行,那内存占用和上下文切换太恐怖; (2)使用线程池一类的,参数设置不到位,根据原理只能一部分一部分执行,想全部执行,得参考(1)的线程数量...

有啥能实现吗?难道类似这就是 go 协程的魅力?

package main

import (
	"strconv"
	"time"
)

func say0(str string) {
	time.Sleep(3 * time.Second)
	println(str)
}

func main() {
	for i := 0; i < 100000; i++ {
		go say0("协程" + strconv.Itoa(i))
	}

	time.Sleep(1000 * time.Second)
}

921 次点击
所在节点    问与答
18 条回复
meiyoumingzi6
2021-12-24 18:23:19 +08:00
其实楼主写的这段代码有大坑🤪
这里 go 不是直接就执行了,所以等到执行的时候 i 的数值是不确定的,另外因为这个 go 执行的太短了,所以在 1000 秒(对不起我一开始看成了 1000 毫秒)是可以执行完的,但是这样是不对的,应该使用 wait group 来处理这个事情,最后 可以无脑 go 吗?理论上来说是可以开启无限个的,但是开太多调度就会很繁忙,还有如果是并发处理请求的话,可能会给对方打挂了,还有一个就是 go 的 func 如果 panic 就会导致整个进程挂掉,所以最好是启动的时候 recover 一下






回到问题上,如果让我换个语言,我会选择线程池,配合 queue 来做😁
leonme
2021-12-24 18:25:39 +08:00
楼主有没有想过协程的应用场景? 照你这么说,java 早被打趴下了,然而……🐶
anonymousar
2021-12-24 18:34:53 +08:00
线程池怎么就做不到了? push 100k 个 task/future 进去不就行了? 这跟线程数有啥关系? go 难道不用 thread 跑任务了?
watzds
2021-12-24 18:38:57 +08:00
我理解就是应用层线程,应用层自己实现

直接用普通线程池,任务互相的话依赖容易死锁,或者阻塞浪费资源,用 java 8 ForkJoinPool 好点
gy123
2021-12-24 18:43:29 +08:00
@meiyoumingzi6 学习了
gy123
2021-12-24 18:45:21 +08:00
@anonymousar push 到队列?然而每次拿出来多少任务执行,还是依靠设置的线程数吧。我知道 go 也是线程,但是这个场景你能做出来吗。可以写写试试
gy123
2021-12-24 18:46:27 +08:00
@watzds 就是我写的这个例子,不知道 java 怎么实现
gy123
2021-12-24 18:47:39 +08:00
我就想知道 java 怎么保证线程数很少的情况下,得到 go 执行的效果。。
iamzuoxinyu
2021-12-24 18:49:20 +08:00
其实跟你自己实现个带任务窃取的线程池是一样的。
gy123
2021-12-24 18:50:24 +08:00
@anonymousar 还是说你想表达的是用延迟线程池,设置个延迟时间然后队列里的任务全部执行。。
gy123
2021-12-24 18:52:49 +08:00
@leonme 场景可能就是大 io 处理上,就算很多轻量级协程,要比线程好太多吧
leonme
2021-12-24 19:30:15 +08:00
@gy123 然后你再想下正常业务瓶颈是在 io 上还是在 cpu 的调度(线程数)上? 所以你就明白为啥 go 一般都是做些网关代理中间件啥的
gy123
2021-12-24 19:33:16 +08:00
@leonme 嗯,这里的 io 特指网络 io.受教了
gy123
2021-12-24 19:37:13 +08:00
此贴终结...楼主忘记了 ScheduledExecutorService
gy123
2021-12-24 19:37:55 +08:00
或者自己实现类似于拿出任务判断时间....被 go 这种 sleep 写法迷惑了
anonymousar
2021-12-24 19:59:28 +08:00
@gy123 延迟跟队列跟线程池都无关 io 密集型任务 goroutine 就好处理么? 明明 epoll 才更优
meiyoumingzi6
2021-12-24 20:01:05 +08:00
#1
emmm 上面说的有点问题, 你这里是传的参数 , 所以 str 不会有问题

@gy123
无脑 go 版本
```golang
package main

import (
"fmt"
"strconv"
"sync"
"time"
)

func say0(str string, wg *sync.WaitGroup) {
defer func() {
_ = recover()
// 处理异常
wg.Done()
}()
time.Sleep(3 * time.Second)
fmt.Println(str)
}

func main() {
wg := &sync.WaitGroup{}
for i := 0; i < 100000; i++ {
wg.Add(1)
go say0("协程" + strconv.Itoa(i), wg)
}
wg.Wait()

}
```


控制协程数量版本

```golang
package main

import (
"fmt"
"strconv"
"sync"
"time"
)

func say0(c chan int, wg *sync.WaitGroup) {
defer func() {
_ = recover()
// 处理异常
wg.Done()
}()
for {
time.Sleep(1 * time.Second)
i, ok := <-c
if ok {
fmt.Println("协程" + strconv.Itoa(i))
} else {
fmt.Println("协程结束")
break
}
}

}

func main() {
var c chan int
c = make(chan int, 10)
wg := &sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go say0(c, wg)
}
for i := 0; i < 100; i++ {
c <- i
}
close(c)
fmt.Printf("close")
wg.Wait()
}

```
gy123
2021-12-25 16:21:33 +08:00
[ [码上开学] 到底什么是「非阻塞式」挂起?协程真的比线程更轻量级吗?-哔哩哔哩] https://b23.tv/YyQhA3Q

我悟了,最后一段说的,的确就是我的误解

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/824293

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX