英文:
How to extend the ticker duration without a race?
问题
我正在尝试实现一个保持连接的机制。问题在于,我不知道如何在没有竞争的情况下替换保持连接的计时器(conn.keepAlive),因为keepAlive()
方法总是从计时器中读取。
//为了简洁起见,未处理错误
const interval = 10 * time.Second
type conn struct {
keepAlive time.Ticker
conn net.Conn
mux sync.Mutex
}
// replace替换底层连接
func (cn conn) replace(newcn net.Conn) {
cn.mux.Lock()
cn.conn = newcn
// 重置计时器
cn.keepAlive.Stop
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
func (cn conn) keepAlive() {
for {
<-cn.keepAlive.C
cn.mux.Lock()
cn.conn.Write([]byte("ping"))
var msg []byte
cn.conn.Read(msg)
if string(msg) != "pong" {
// 做一些坏事
}
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
}
英文:
I'm trying to implement a keepAlive mechanism. The issue is that I don't know how to replace the keep alive ticker ( conn.keepAlive) without a race because keepAlive()
method always reads from the ticker.
//errors not handled for brevity
const interval = 10 * time.Second
type conn struct {
keepAlive time.Ticker
conn net.Conn
mux sync.Mutex
}
// replace replaces the underlying connection
func (cn conn) replace(newcn net.Conn) {
cn.mux.Lock()
cn.conn = newcn
// reset the ticker
cn.keepAlive.Stop
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
func (cn conn) keepAlive() {
for {
<-cn.keepAlive.C
cn.mux.Lock()
cn.conn.Write([]byte("ping"))
var msg []byte
cn.conn.Read(msg)
if string(msg) != "pong" {
// do some mean stuff
}
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
}
答案1
得分: 0
一种更简洁的实现方式是使用通道作为同步机制,而不是互斥锁:
type conn struct {
sync.Mutex
conn net.Conn
replaceConn chan net.Conn
}
// replace replaces the underlying connection
func (cn *conn) replace(newcn net.Conn) {
cn.replaceConn <- newcn
}
func (cn *conn) keepAlive() {
t := time.NewTicker(interval)
msg := make([]byte, 10)
for {
select {
case <-t.C:
case newConn := <-cn.replaceConn:
cn.Lock()
cn.conn = newConn
cn.Unlock()
continue
}
cn.Lock()
_ = msg
// do keepalive
cn.Unlock()
}
}
以上是代码的翻译结果。
英文:
One way to implement this much more succinctly, is by using a channel as the synchronization mechanism, instead of the mutex:
type conn struct {
sync.Mutex
conn net.Conn
replaceConn chan net.Conn
}
// replace replaces the underlying connection
func (cn *conn) replace(newcn net.Conn) {
cn.replaceConn <- newcn
}
func (cn *conn) keepAlive() {
t := time.NewTicker(interval)
msg := make([]byte, 10)
for {
select {
case <-t.C:
case newConn := <-cn.replaceConn:
cn.Lock()
cn.conn = newConn
cn.Unlock()
continue
}
cn.Lock()
_ = msg
// do keepalive
cn.Unlock()
}
}
答案2
得分: -2
我最终得到了下面的代码。我对它的外观不太满意,但它是有效的。基本上,我在一个通道中包装了mux,这样我就可以在其上进行选择。
const interval = 10 * time.Second
type conn struct {
keepAlive time.Ticker
conn *net.Conn
mux sync.Mutex
}
// replace replaces the underlying connection
func (cn conn) replace(newcn *net.Conn) {
cn.mux.Lock()
cn.conn = newcn
// reset the ticker
cn.keepAlive.Stop()
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
func (cn conn) keepAlive() {
lockerFn := func() <-chan struct{} {
cn.mux.Lock()
ch := make(chan struct{})
go func() {
ch <- struct{}{}
}()
return ch
}
for {
locker := lockerFn()
select {
case <-cn.keepAlive.C:
// unlock the locker otherwise we
// get stuck
go func() {
<-locker
cn.mux.Unlock()
}()
case <-locker:
cn.conn.Write([]byte("ping"))
var msg []byte
cn.conn.Read(msg)
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
}
}
以上是翻译好的代码部分。
英文:
I ended up with the code below. I'm not very happy how it looks like but it works. Basically I wrapped the mux in a channel so that I can do a select on it.
const interval = 10 * time.Second
type conn struct {
keepAlive time.Ticker
conn *net.Conn
mux sync.Mutex
}
// replace replaces the underlying connection
func (cn conn) replace(newcn *net.Conn) {
cn.mux.Lock()
cn.conn = newcn
// reset the ticker
cn.keepAlive.Stop
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
func (cn conn) keepAlive() {
lockerFn := func() <-chan struct{} {
cn.mux.Lock()
ch = make(chan struct{})
go func() {
ch <- struct{}{}
}()
return ch
}
for {
locker := lockerFn()
select {
case <-cn.keepAlive.C:
// unlock the locker otherwise we
// get stuck
go func() {
<-locker
cn.mux.Unlock()
}()
case <-locker:
cn.conn.Write([]byte("ping"))
var msg []byte
cn.conn.Read(msg)
cn.keepAlive = time.NewTicker(interval)
cn.mux.Unlock()
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论