为什么在我使用gorm连接数据库后,chan的行为会发生变化?

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

Why does the behavior of chan change after I use gorm to connect to the database

问题

一个正常的例子是这样的:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)
	func() {
		time.Sleep(1)
		ch <- 1
	}()

	fmt.Println(<-ch)
}

输出应该是:

# go run test.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main.func1(...)
	/root/test.go:17
main.main()
	/root/test.go:18 +0x47
exit status 2

但是当我添加连接数据库的代码时,像这样:

package main

import (
	"fmt"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	db, err := gorm.Open(mysql.Open("dsn"))
	fmt.Printf("%v, %s\n", db, err)

	ch := make(chan int)
	func() {
		time.Sleep(1)
		ch <- 1
	}()

	fmt.Println(<-ch)
}

它不会报错,而是一直阻塞,输出结果是(我用Ctrl+C结束了它):

# go run test.go
&{0xc00015c510 <nil> 0 0xc00016c380 1}, %!s(<nil>)
^Csignal: interrupt

有人可以帮助我吗,谢谢。

英文:

A normal example is this:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	ch := make(chan int)
	func() {
		time.Sleep(1)
		ch &lt;- 1
	}()

	fmt.Println(&lt;-ch)
}

The output should be:

# go run test.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main.func1(...)
	/root/test.go:17
main.main()
	/root/test.go:18 +0x47
exit status 2

But when I add the code to connect to the database like this:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;

    &quot;gorm.io/driver/mysql&quot;
    &quot;gorm.io/gorm&quot;
)

func main() {
	db, err := gorm.Open(mysql.Open(&quot;dsn&quot;))
	fmt.Printf(&quot;%v, %s\n&quot;, db, err)

	ch := make(chan int)
	func() {
		time.Sleep(1)
		ch &lt;- 1
	}()

	fmt.Println(&lt;-ch)
}

It doesn't report an error, it keeps blocking, the output is(I ended it with ctrl+c):

# go run test.go
&amp;{0xc00015c510 &lt;nil&gt; 0 0xc00016c380 1}, %!s(&lt;nil&gt;)
^Csignal: interrupt

Can anyone help me, thanks.

答案1

得分: 1

在连接的数据库情况下,它不会报告“所有goroutine都处于休眠状态”,因为并非所有goroutine都处于休眠状态。

gorm和db会启动它们自己的goroutine,这些goroutine在等待发送接收或执行其他操作。

如果在这两种情况下都执行ctrl+\,这些goroutine将会显示出来。

  1. 未连接数据库:

    goroutine 0 [空闲]: runtime.epollwait()
    /usr/lib/golang/src/runtime/sys_linux_amd64.s:699 +0x20 runtime.netpoll(0xc000032500?)
    /usr/lib/golang/src/runtime/netpoll_epoll.go:126 +0xdc

  2. 已连接数据库:

goroutine 0 [空闲]:
runtime.futex()
        /usr/lib/golang/src/runtime/sys_linux_amd64.s:552 +0x21
runtime.futexsleep(0x10000000000?, 0xcb927f8?, 0x7fffc45f8fa8?)
        /usr/lib/golang/src/runtime/os_linux.go:66 +0x36

goroutine 1 [通道发送]:
main.main.func1(...)

goroutine 19 [select]:
database/sql.(*DB).connectionOpener(0xc00019ea90, {0x81af60, 0xc0001972c0})

goroutine 20 [select]:
github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1()
英文:

It does not report all goroutines are asleep in case of connected DB because all goroutines are not asleep.

gorm and db start their own goroutines that are waiting to send receive, doing other stuff.

if you do a ctrl+\ in both cases these goroutines will be shown

1 No DB connected

goroutine 0 [idle]: runtime.epollwait()
        /usr/lib/golang/src/runtime/sys_linux_amd64.s:699 +0x20 runtime.netpoll(0xc000032500?)
        /usr/lib/golang/src/runtime/netpoll_epoll.go:126 +0xdc
  1. DB connected
goroutine 0 [idle]:
runtime.futex()
        /usr/lib/golang/src/runtime/sys_linux_amd64.s:552 +0x21
runtime.futexsleep(0x10000000000?, 0xcb927f8?, 0x7fffc45f8fa8?)
        /usr/lib/golang/src/runtime/os_linux.go:66 +0x36

goroutine 1 [chan send]:
main.main.func1(...)

goroutine 19 [select]:
database/sql.(*DB).connectionOpener(0xc00019ea90, {0x81af60, 0xc0001972c0})

goroutine 20 [select]:
github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1()

huangapple
  • 本文由 发表于 2022年9月4日 16:32:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/73597882.html
匿名

发表评论

匿名网友

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

确定