More accurate ticker than time.NewTicker in go on macOS

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

More accurate ticker than time.NewTicker in go on macOS

问题

我正在编写控制外部LED的代码,通过UDP进行控制,并且需要保持尽可能恒定的帧率(例如60Hz)。太多的抖动会看起来很糟糕。我使用time.NewTicker编写了一个简单的测试,结果并不理想。我想知道是否有一种不同的方法可以在更准确的间隔上执行代码。这个测试在macOS上运行,但也需要在Windows和Linux上运行。值得一提的是,它需要与音频同步,所以也许每个操作系统上都有一个可以与音频同步的音频ticker API呢?

package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"
)

var f *os.File
var testTimeSeconds = 30

func appendToCsv(t time.Time) {
	w := csv.NewWriter(f)
	defer w.Flush()

	records := []string{strconv.FormatInt(t.UnixMicro(), 10)}
	w.Write(records)
}

func init() {
	var err error
	f, err = os.Create("newTicker.csv")
	if err != nil {
		log.Fatal(err)
	}

	w := csv.NewWriter(f)
	defer w.Flush()

	records := []string{"timestamps"}
	w.Write(records)
}

func main() {
	usPerFrame := 16666
	ticker := time.NewTicker(time.Duration(usPerFrame) * time.Microsecond)
	defer ticker.Stop()
	done := make(chan bool)
	go func() {
		time.Sleep(time.Duration(testTimeSeconds) * time.Second)
		done <- true
	}()
	for {
		select {
		case <-done:
			fmt.Println("Done!")
			return
		case t := <-ticker.C:
			appendToCsv(t)
		}
	}
}

更新:
我进行了另一个测试,比较了第一种方法和@jochen答案中的方法,但仍然不太准确。

More accurate ticker than time.NewTicker in go on macOS

更新:
我进行了另一个测试,比较了第一种方法和@jochen答案中的方法,但仍然不太准确。

More accurate ticker than time.NewTicker in go on macOS

英文:

I am writing code to control external LEDs over UDP, and need to keep a frame rate (e.g. 60Hz) that is as constant as possible. Too much jitter will look bad. I've written a simple test using time.NewTicker and the results are not ideal. I'm wondering if there is a different way to execute code on a more accurate interval. This test was run on macOS, but needs to run on Windows and Linux too. For what it's worth, it needs to sync to audio, so maybe there is an audio ticker API on each OS it could potentially sync with?

package main

import (
	&quot;encoding/csv&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;strconv&quot;
	&quot;time&quot;
)

var f *os.File
var testTimeSeconds = 30

func appendToCsv(t time.Time) {

	w := csv.NewWriter(f)
	defer w.Flush()

	records := []string{strconv.FormatInt(t.UnixMicro(), 10)}
	w.Write(records)
}

func init() {
	var err error
	f, err = os.Create(&quot;newTicker.csv&quot;)
	if err != nil {
		log.Fatal(err)
	}

	w := csv.NewWriter(f)
	defer w.Flush()

	records := []string{&quot;timestamps&quot;}
	w.Write(records)
}

func main() {
	usPerFrame := 16666
	ticker := time.NewTicker(time.Duration(usPerFrame) * time.Microsecond)
	defer ticker.Stop()
	done := make(chan bool)
	go func() {
		time.Sleep(time.Duration(testTimeSeconds) * time.Second)
		done &lt;- true
	}()
	for {
		select {
		case &lt;-done:
			fmt.Println(&quot;Done!&quot;)
			return
		case t := &lt;-ticker.C:
			appendToCsv(t)
		}
	}
}

More accurate ticker than time.NewTicker in go on macOS

UPDATE:
I ran another test comparing the first method with the method in @jochen's answer, still not very accurate.

More accurate ticker than time.NewTicker in go on macOS

答案1

得分: 1

一种想法是使用time.Sleep()而不是使用ticker。这样可以将通道的发送/接收操作从循环中移除,可能会导致更准确的定时。为了实现这一点,你可以在一个单独的goroutine中运行以下函数:

func ticker(step time.Duration, done <-chan struct{}) {
    next := time.Now().Add(step)
    for {
        time.Sleep(time.Until(next))
        appendToCsv(time.Now())

        select { // 检查`done`是否已关闭
        case <-done:
            return
        default:
            // 无操作
        }

        next = next.Add(step)
    }
}
英文:

One idea would be to just use time.Sleep() instead of a ticker. This takes the channel send/receive out of the loop and may lead to more accurate timing. To do this, you could run a function like the following in a separate goroutine:

func ticker(step time.Duration, done &lt;-chan struct{}) {
        next := time.Now().Add(step)
        for {
                time.Sleep(time.Until(next))
                appendToCsv(time.Now())

                select { // check whether `done` was closed
                case &lt;-done:
                        return
                default:
                        // pass
                }

                next = next.Add(step)
        }
}

huangapple
  • 本文由 发表于 2022年1月5日 22:42:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/70594795.html
匿名

发表评论

匿名网友

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

确定