measure execution time and stop waiting if too long in golang

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

measure execution time and stop waiting if too long in golang

问题

我正在尝试测量funcWithUnpredictiveExecutionTime函数的执行时间。

func measureTime(expectedMs float64) (ok bool) {
    t1 := time.Now()
    funcWithUnpredictiveExecutionTime()
    t2 := time.Now()
    diff := t2.Sub(t1)

funcWithUnpredictiveExecutionTime的执行速度比我预期的要快时,测量是正常的。但是,如果它的执行速度比expectedMs要慢,那么测量将不会在预期的毫秒数过去后立即停止。

是否有可能在funcWithUnpredictiveExecutionTime的执行时间超过expectedMs时停止计时,而不必等待funcWithUnpredictiveExecutionTime执行完毕?

换句话说,measureTime(200)无论结果好坏,都应该在200毫秒内返回。

我猜我应该使用通道,然后以某种方式取消等待通道。但是具体该如何做呢?

完整代码:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

// random number between min and max
func random(min, max int) int {
    rand.Seed(time.Now().Unix())
    return rand.Intn(max-min) + min
}

// sleeps for a random milliseconds amount between 200 and 1000
func funcWithUnpredictiveExecutionTime() {
    millisToSleep := random(200, 1000)
    fmt.Println(fmt.Sprintf("Sleeping for %d milliseconds", millisToSleep))
    time.Sleep(time.Millisecond * time.Duration(millisToSleep))
}

// measures execution time of a function funcWithUnpredictiveExecutionTime
// if expectedMs < actual execution time, it's ok.
// if expectedMs milliseconds passed and funcWithUnpredictiveExecutionTime
// still did not finish execution it should return
// without waiting for funcWithUnpredictiveExecutionTime
func measureTime(expectedMs float64) (ok bool) {
    t1 := time.Now()
    funcWithUnpredictiveExecutionTime()
    t2 := time.Now()
    diff := t2.Sub(t1)
    actualMs := diff.Seconds() * 1000
    ok = actualMs < expectedMs
    fmt.Println(actualMs)
    return
}

// prints results: Ok or too late
func printTimeResults(ok bool) {
    if ok {
        fmt.Println("Ok")
    } else {
        fmt.Println("Too late")
    }
}

func main() {
    printTimeResults(measureTime(200))  // expect it to finish in 200 ms anyway
    printTimeResults(measureTime(1000)) // expect it to finish in 1000 ms anyway
}

输出:

Sleeping for 422 milliseconds
424.11895200000004
Too late
Sleeping for 422 milliseconds
425.27274900000003
Ok

Playground

英文:

I am trying to measure execution time of funcWithUnpredictiveExecutionTime function.

func measureTime(expectedMs float64) (ok bool) {
    t1 := time.Now()
	funcWithUnpredictiveExecutionTime()
	t2 := time.Now()
	diff := t2.Sub(t1)

The measuring is fine when funcWithUnpredictiveExecutionTime works faster than I expected. But if it works slower than expectedMs the measuring will not stop right after expected amount of milliseconds passed.

Is it possible to stop time measuring when funcWithUnpredictiveExecutionTime works longer than expectedMs without waiting funcWithUnpredictiveExecutionTime to finish?

In other words, measureTime(200) should return in 200 ms anyway with a good or bad result.

I guess I should use channels and then somehow cancel waiting for a channel. But how to do it exactly?

Full code:

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

// random number between min and max
func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

// sleeps for a random milliseconds amount between 200 and 1000
func funcWithUnpredictiveExecutionTime() {
	millisToSleep := random(200, 1000)
	fmt.Println(fmt.Sprintf(&quot;Sleeping for %d milliseconds&quot;, millisToSleep))
	time.Sleep(time.Millisecond * time.Duration(millisToSleep))
}

// measures execution time of a function funcWithUnpredictiveExecutionTime
// if expectedMs &lt; actual execution time, it&#39;s ok.
// if expectedMs milliseconds passed and funcWithUnpredictiveExecutionTime
// still did not finish execution it should return
// without waiting for funcWithUnpredictiveExecutionTime
func measureTime(expectedMs float64) (ok bool) {
	t1 := time.Now()
	funcWithUnpredictiveExecutionTime()
	t2 := time.Now()
	diff := t2.Sub(t1)
	actualMs := diff.Seconds() * 1000
	ok = actualMs &lt; expectedMs
	fmt.Println(actualMs)
	return
}

// prints results: Ok or too late
func printTimeResults(ok bool) {
	if ok {
		fmt.Println(&quot;Ok&quot;)
	} else {
		fmt.Println(&quot;Too late&quot;)
	}
}

func main() {
	printTimeResults(measureTime(200))  // expect it to finish in 200 ms anyway
	printTimeResults(measureTime(1000)) // expect it to finish in 1000 ms anyway
}

Output:

Sleeping for 422 milliseconds
424.11895200000004
Too late
Sleeping for 422 milliseconds
425.27274900000003
Ok

Playground

答案1

得分: 7

你无法取消一个goroutine,除非你设计它可以被取消。你可以通过使用一个通道来信号化函数完成来提前结束你的计时函数:

func measureTime(expectedMs float64) (ok bool) {
    done := make(chan struct{})
    t1 := time.Now()

    go func() {
        funcWithUnpredictiveExecutionTime()
        close(done)
    }()

    select {
    case <-done:
        ok = true
    case <-time.After(time.Duration(expectedMs) * time.Millisecond):
    }

    fmt.Println(time.Since(t1))
    return ok
}

这段代码会测量函数funcWithUnpredictiveExecutionTime的执行时间,并在预期的时间内完成时返回true,否则返回false

英文:

You can't cancel a goroutine, unless you design it to be canceled. You can short circuit your timing function, by using a channel to signal the completion of the function being timed:

func measureTime(expectedMs float64) (ok bool) {
	done := make(chan struct{})
	t1 := time.Now()

	go func() {
		funcWithUnpredictiveExecutionTime()
		close(done)
	}()

	select {
	case &lt;-done:
		ok = true
	case &lt;-time.After(time.Duration(expectedMs) * time.Millisecond):
	}

	fmt.Println(time.Since(t1))
	return ok

}

答案2

得分: 3

扩展JimB的示例,我个人遵循的异步后台工作设计。我认为,在大多数情况下,启动一个go例程而不传递中止通道是不可接受的...所有异步方法都应该接受一个中止通道作为参数,或者在其接收类型上定义一个中止通道,以便您可以实际控制执行。顺便说一下,有一些库可以实现这个功能,这是一个我以前的同事制作的简单库:https://github.com/lytics/squaredance

如果您的程序没有为每个goroutine提供中止路径,那么您可能会面临重大的质量问题,早晚会出现。此外,对于在goroutine中进行任何繁重工作的应用程序,您可能无法优雅地停止和启动应用程序。

func measureTime(expectedMs float64) (ok bool) {
    done := make(chan struct{})
    abort := make(chan struct{})
    t1 := time.Now()

    go func() {
        funcWithUnpredictiveExecutionTime(abort)
        close(done)
    }()

    select {
    case <-done:
        ok = true
    case <-time.After(time.Duration(expectedMs) * time.Millisecond):
        // 当达到指定的持续时间时,关闭abort通道
        close(abort)
    }

    fmt.Println(time.Since(t1))
    return ok
}


func funcWithUnpredictiveExecutionTime(abort chan struct{}) {
    for {
        select {
        // 在这里进行工作
        case <-abort:
            // 除了这里,我们进行清理并返回
        }
    }
}
英文:

Extending JimB's example a little with the design I've personally followed for async background workers. I would say in most cases it's unacceptable to launch a go routine without passing an abort channel... All of your async methods should accept one as an argument or have one defined on their receiving type so you can actually control execution. fyi there are libraries for this, here's a simple one an old colleague of mine made; https://github.com/lytics/squaredance

If your program does not have an abort path for every goroutine you're probably going to face significant quality issues sooner or later. Also, for applications that are doing any heavy lifting in a goroutine, you will likely not be able to gracefully stop and start your application.

func measureTime(expectedMs float64) (ok bool) {
    done := make(chan struct{})
    abort := make(chan struct{})
    t1 := time.Now()

    go func() {
        funcWithUnpredictiveExecutionTime(abort)
        close(done)
    }()

    select {
    case &lt;-done:
        ok = true
    case &lt;-time.After(time.Duration(expectedMs) * time.Millisecond):
        // after signals here when the duration is reached so I close abort
        close(abort)
    }

    fmt.Println(time.Since(t1))
    return ok

}


funcWithUnpredictiveExecutionTime(abort) {
     for {
        select {
           // doing work in here
           case abort:
              // except here, we clean up and return
        }
     }
}

huangapple
  • 本文由 发表于 2015年6月15日 23:42:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/30849375.html
匿名

发表评论

匿名网友

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

确定