如何在没有竞争的情况下延长计时器的持续时间?

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

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 {
		&lt;-cn.keepAlive.C
		cn.mux.Lock()
		cn.conn.Write([]byte(&quot;ping&quot;))
		var msg []byte
		cn.conn.Read(msg)
		if string(msg) != &quot;pong&quot; {
			// 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 &lt;- newcn
}

func (cn *conn) keepAlive() {
	t := time.NewTicker(interval)
	msg := make([]byte, 10)

	for {
		select {
		case &lt;-t.C:
		case newConn := &lt;-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() &lt;-chan struct{} {
		cn.mux.Lock()
		ch = make(chan struct{})
		go func() {
			ch &lt;- struct{}{}
		}()
		return ch
	}
	for {
		locker := lockerFn()
		select {
		case &lt;-cn.keepAlive.C:
			// unlock the locker otherwise we
			// get stuck
			go func() {
				&lt;-locker
				cn.mux.Unlock()
			}()
		case &lt;-locker:
			cn.conn.Write([]byte(&quot;ping&quot;))
			var msg []byte
			cn.conn.Read(msg)
			cn.keepAlive = time.NewTicker(interval)
			cn.mux.Unlock()
		}
	}
}

huangapple
  • 本文由 发表于 2015年8月13日 03:00:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/31973345.html
匿名

发表评论

匿名网友

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

确定