2013-07-22 29 views
10

如果我正在通过股票频道调用stop(),则通道已停止但未关闭。Golang中的股票停止行为

在本例中:

package main 

import (
    "time" 
    "log" 
) 

func main() { 
    ticker := time.NewTicker(1 * time.Second) 
    go func(){ 
     for _ = range ticker.C { 
      log.Println("tick") 
     } 
     log.Println("stopped") 
    }() 
    time.Sleep(3 * time.Second) 
    log.Println("stopping ticker") 
    ticker.Stop() 
    time.Sleep(3 * time.Second) 
} 

运行产生:

2013/07/22 14:26:53 tick 
2013/07/22 14:26:54 tick 
2013/07/22 14:26:55 tick 
2013/07/22 14:26:55 stopping ticker 

这样够程永远不会退出。有没有更好的方法来处理这种情况?我应该关心这个门厅从不退出吗?

+1

如果执行程序没有退出,则会发生内存泄漏。调用close(ticker.C)以释放Go例程。 – fuz

+3

无法关闭它:“无法关闭只接收频道” – whatupdave

回答

10

在第二个通道上发出“完成”信号,并在您的goroutine中选择ticker和done通道之间的通道。

根据你真正想要做的更好的解决方案可能存在,但这很难从简化的演示代码中知道。

11

沃尔克建议使用第二个通道。这是我最终运行的:

package main 

import (
    "log" 
    "time" 
) 

// Run the function every tick 
// Return false from the func to stop the ticker 
func Every(duration time.Duration, work func(time.Time) bool) chan bool { 
    ticker := time.NewTicker(duration) 
    stop := make(chan bool, 1) 

    go func() { 
     defer log.Println("ticker stopped") 
     for { 
      select { 
      case time := <-ticker.C: 
       if !work(time) { 
        stop <- true 
       } 
      case <-stop: 
       return 
      } 
     } 
    }() 

    return stop 
} 

func main() { 
    stop := Every(1*time.Second, func(time.Time) bool { 
     log.Println("tick") 
     return true 
    }) 

    time.Sleep(3 * time.Second) 
    log.Println("stopping ticker") 
    stop <- true 
    time.Sleep(3 * time.Second) 
} 
+4

如果您的工作需要4秒钟,则会导致您的goroutine死锁,并且它会被卡住,试图写入频道,这是唯一的读者。你真的只想在'for {}'上使用一个状态变量 - 并且不要在停止通道上发送,只需关闭它即可。 – Dustin

-1

如果您需要节省空结构的更多空间使用通道 - struct {},它不需要内存。正如上面提到的,不要发送一些东西 - 只要关闭,实际上发送零值。

0

你可以这样做。

package main 

import (
    "fmt" 
    "time" 
) 

func startTicker(f func()) chan bool { 
    done := make(chan bool, 1) 
    go func() { 
     ticker := time.NewTicker(time.Second * 1) 
     defer ticker.Stop() 
     for { 
      select { 
      case <-ticker.C: 
       f() 
      case <-done: 
       fmt.Println("done") 
       return 
      } 
     } 
    }() 
    return done 
} 

func main() { 
    done := startTicker(func() { 
     fmt.Println("tick...") 
    }) 
    time.Sleep(5 * time.Second) 
    close(done) 
    time.Sleep(5 * time.Second) 
}