V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
gy123
V2EX  ›  问与答

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

  •  
  •   gy123 · 2021-12-24 17:55:38 +08:00 · 911 次点击
    这是一个创建于 846 天前的主题,其中的信息可能已经有所发展或是发生改变。

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






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

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

    我悟了,最后一段说的,的确就是我的误解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3434 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 11:18 · PVG 19:18 · LAX 04:18 · JFK 07:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.