【问题标题】:Strange behaviour with SMTP within goroutinegoroutine 中 SMTP 的奇怪行为
【发布时间】:2016-06-05 01:44:32
【问题描述】:

我对在 goroutine 中使用 SMTP 时发现的一些奇怪行为感到困惑。

当尝试使用以下代码发送电子邮件时,我的 SendMail 功能启动(我可以看到一条日志消息)但从未完成。它只是挂起。

func main() {
    go SendEmail("TEST")
    for {}
  }

但是,当我向 SendMail 添加另一个呼叫时 - 两封电子邮件都已成功发送。

func main() {
    go SendEmail("TEST")
    SendEmail("TEST")
    for {}
  }

谁能解释一下是怎么回事?

注意,这是一个非常简化的计划任务版本,因此是 while 循环

发送电子邮件的代码:

func SendEmail(message string) {
  log.Print("Sending email")
  from, password, to := "me@gmail.com", "PASSWORD", "me@gmail.com"

  err := smtp.SendMail(
    "smtp.gmail.com:587",
    smtp.PlainAuth("", from, password, "smtp.gmail.com"),
    from,
    []string{to},
    []byte(message),
    )
if err != nil {
    log.Fatal(err)
}
  log.Print("Sending complete")
}

谢谢!

【问题讨论】:

标签: go concurrency


【解决方案1】:

您应该将for{} 替换为select{}。永远不要创建一个空的for{}

for{} 创建了一个永远旋转的循环,但由于循环中没有函数调用(或更准确地说是堆栈检查和/或分配),调度程序将永远无法取消调度主 goroutine,它在turn 可以防止您的其他 goroutine 被调度。 (这也可能会吃掉整个 CPU,因为您忙于循环操作系统线程)

请参阅我最后关于 GOMAXPROCS 的说明。

通过在您的go SendEmail("TEST") 之后调用SendEmail("TEST"),运行时有机会(或者更确切地说,多次机会)安排您的另一个goroutine 运行,然后再进入您的for{} 循环。

使用空选择 (select{}) 代替,主 goroutine 将无限期等待而不消耗任何 CPU,而是直接让步给调度程序。

关于 GOMAXPROCS

如果您的 GOMAXPROCS 为 1(Go 1.5 之前的默认值),您将只有一个 OS 线程(至少对于运行 goroutines),如果您有一个忙循环,这将阻止任何其他 goroutines 运行。如果您的 GOMAXPROCS > 1 运行时仍然可以调度其他 goroutine,因为运行 goroutine 的线程不止一个,但您不应该依赖它。相反,您应该删除所有繁忙循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-25
    相关资源
    最近更新 更多