Go超时未触发

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

Go timeout not firing

问题

我用Go语言实现了几种排序算法,只是为了好玩,现在我想测试它们在随机整数上的性能。所以我写了下面的程序。我按照类似的格式编写了代码,参考了这个网站:https://gobyexample.com/timeouts

然而,似乎超时没有正确触发。以下是我的代码:

package main

import (
	"allanpinkerton.com/algorithms/sorting"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
)

// 打印从开始到现在经过的时间
func timeExecution(startTime time.Time, functionName string, inputSize int) string {
	executionTime := time.Since(startTime)
	return fmt.Sprintf("%-20s took %10.4fms to sort %d elements\n", functionName, float64(executionTime.Nanoseconds())/1000000, inputSize)
}

// 生成包含n个随机整数的文件,文件名为integerArray + n
func generateRandomIntegers(n int, filename string) {
	arr := make([]int, n)
	for i := 0; i < n; i++ {
		arr[i] = rand.Int()
	}
	f, _ := os.Create(filename)
	defer f.Close()
	for _, num := range arr {
		f.WriteString(strconv.Itoa(num) + " ")
	}
	f.WriteString("\n")
	f.Sync()
	fmt.Printf("Generated " + filename + " with " + strconv.Itoa(n) + " elements.\n")
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {

	sortingFunctions := map[string]interface{}{
		"InsertionSort":        sorting.InsertionSort,
		"QuickSortLastElement": sorting.QuickSortLastElement,
		"QuickSortRandom":      sorting.QuickSortRandom,
	}
	if len(os.Args) != 2 {
		fmt.Printf("No size specified.\n")
		return
	}
	size := os.Args[1]
	sizeInt, err := strconv.Atoi(size)
	checkError(err)

	arr := make([]int, sizeInt)
	for i := 0; i < sizeInt; i++ {
		arr[i] = rand.Int()
	}
	fmt.Println("Generated " + size + " integers.")

	mainChannel := make(chan string)
	for k, v := range sortingFunctions {
		newArr := make([]int, len(arr))
		copy(newArr, arr)
		go func(name string, v interface{}) {
			start := time.Now()
			v.(func([]int))(newArr)
			result := timeExecution(start, name, len(newArr))
			mainChannel <- result
		}(k, v)
	}

	for _ = range sortingFunctions {
		select {
		case result := <-mainChannel:
			fmt.Printf(result)
		case <-time.After(time.Second):
			fmt.Println("Timeout")
		}
	}

	return
}

顶部是一些辅助函数,但是主函数似乎有些问题。我运行了go install,并对150,000个元素运行了该程序,得到了下面的响应:

Generated 150000 integers.
QuickSortLastElement took    15.0037ms to sort 150000 elements
InsertionSort        took  7599.5884ms to sort 150000 elements
QuickSortRandom      took    15.1697ms to sort 150000 elements

显然,插入排序花费了超过7秒的时间,但是超时应该在1秒后触发。有没有什么原因导致超时没有触发呢?

所以我尝试将自定义的排序程序替换为sort包中的sort.Ints函数,将sortingFunctions的类型更改为:

sortingFunctions := map[string]func([]int){
	"InsertionSort":        sort.Ints,
	"QuickSortLastElement": sort.Ints,
	"QuickSortRandom":      sort.Ints,
}

问题得到了解决。所以是我的自定义排序函数阻止了超时的触发。我需要在这些函数中添加一些内容才能使它们并行运行吗?

以下是在playground中包含所有代码的链接:
https://play.golang.org/p/SBgDTGyUyp

英文:

I implemented a few sorting algorithms in Go for fun, and now I'd like to test their performance on random integers. So I wrote the following program. I followed a similar format to: https://gobyexample.com/timeouts

However, it seems like the timeout is not firing properly. Below is my code:

package main
import (
&quot;allanpinkerton.com/algorithms/sorting&quot;
&quot;fmt&quot;
&quot;math/rand&quot;
&quot;os&quot;
&quot;strconv&quot;
&quot;time&quot;
)
// Prints out the time elapsed since start
func timeExecution(startTime time.Time, functionName string, inputSize int) string {
executionTime := time.Since(startTime)
return fmt.Sprintf(&quot;%-20s took %10.4fms to sort %d elements\n&quot;, functionName, float64(executionTime.Nanoseconds())/1000000, inputSize)
}
// Generates file with n random ints named integerArray + n
func generateRandomIntegers(n int, filename string) {
arr := make([]int, n)
for i := 0; i &lt; n; i++ {
arr[i] = rand.Int()
}
f, _ := os.Create(filename)
defer f.Close()
for _, num := range arr {
f.WriteString(strconv.Itoa(num) + &quot; &quot;)
}
f.WriteString(&quot;\n&quot;)
f.Sync()
fmt.Printf(&quot;Generated &quot; + filename + &quot; with &quot; + strconv.Itoa(n) + &quot; elements.\n&quot;)
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
func main() {
sortingFunctions := map[string]interface{}{
&quot;InsertionSort&quot;:        sorting.InsertionSort,
&quot;QuickSortLastElement&quot;: sorting.QuickSortLastElement,
&quot;QuickSortRandom&quot;:      sorting.QuickSortRandom,
}
if len(os.Args) != 2 {
fmt.Printf(&quot;No size specified.\n&quot;)
return
}
size := os.Args[1]
sizeInt, err := strconv.Atoi(size)
checkError(err)
arr := make([]int, sizeInt)
for i := 0; i &lt; sizeInt; i++ {
arr[i] = rand.Int()
}
fmt.Println(&quot;Generated &quot; + size + &quot; integers.&quot;)
mainChannel := make(chan string)
for k, v := range sortingFunctions {
newArr := make([]int, len(arr))
copy(newArr, arr)
go func(name string, v interface{}) {
start := time.Now()
v.(func([]int))(newArr)
result := timeExecution(start, name, len(newArr))
mainChannel &lt;- result
}(k, v)
}
for _ = range sortingFunctions {
select {
case result := &lt;-mainChannel:
fmt.Printf(result)
case &lt;-time.After(time.Second):
fmt.Println(&quot;Timeout&quot;)
}
}
return
}

The top is just a bunch of helpers, but the there's something funny going on with the main function. I ran go install and ran it against 150,000 elements, and got the response below:

Generated 150000 integers.
QuickSortLastElement took    15.0037ms to sort 150000 elements
InsertionSort        took  7599.5884ms to sort 150000 elements
QuickSortRandom      took    15.1697ms to sort 150000 elements

Clearly insertion sort took over 7 seconds, but the timeout should fire after 1 second. Is there any reason for the timeout not to fire?

So I tried switching out my custom sorting program out for the sort.Ints function from the sort package by changing sortingFuncs map into:

sortingFunctions := map[string]func([]int){
&quot;InsertionSort&quot;:        sort.Ints,
&quot;QuickSortLastElement&quot;: sort.Ints,
&quot;QuickSortRandom&quot;:      sort.Ints,
}

And the problem was solved. So it's my custom sorting functions that are preventing the timeout from being fired. Is there something that I have to add to those functions to make them run in parallel?

Here is a concatenated version with all the code in the playground.
https://play.golang.org/p/SBgDTGyUyp

答案1

得分: 1

由于您在最终循环中使用了time.After(time.Second),每次收到结果时都会重置超时时间。相反,可以尝试以下代码:

timeoutChannel := time.After(time.Second)
for _ = range sortingFunctions {
    select {
    case result := <-mainChannel:
        fmt.Printf(result)
    case <-timeoutChannel:
        fmt.Println("Timeout")
    }
}

上述代码现在可以正确捕获超时。但它仍然不完全按预期工作,因为循环的内容总是执行三次(因为sortingFunctions有三个元素),并且任何超时都计入这三次迭代。使用您在Go Playground中的代码,我现在得到以下输出:

Generated 90000 integers.
QuickSortRandom      took     9.4268ms to sort 90000 elements
Timeout
QuickSortLastElement took     8.6096ms to sort 90000 elements

请注意,这只是翻译的结果,我无法回答关于代码的问题。

英文:

Since you use time.After(time.Second) inside the final loop, you are reseting the timeout every time a result arrives. Instead, try

timeoutChannel := time.After(time.Second)
for _ = range sortingFunctions {
select {
case result := &lt;-mainChannel:
fmt.Printf(result)
case &lt;-timeoutChannel:
fmt.Println(&quot;Timeout&quot;)
}
}

The above code now catches the timeout correctly. It still does not work quite as intended, because the contents of the loop always are executed three times (since sortingFunctions has three elements), and any timeout counts towards these three iterations. Using your code from the go playground I now get the following output:

Generated 90000 integers.
QuickSortRandom      took     9.4268ms to sort 90000 elements
Timeout
QuickSortLastElement took     8.6096ms to sort 90000 elements

答案2

得分: 0

你在Go Playground上发布的代码产生了以下输出:

生成了90000个整数。
插入排序花费4465.6230毫秒对90000个元素进行排序
以最后一个元素为基准的快速排序花费11.2758毫秒对90000个元素进行排序
随机选择基准的快速排序花费11.6547毫秒对90000个元素进行排序

我怀疑你没有看到超时被调用的原因是因为插入排序没有进行任何函数调用,因此不允许调度程序在goroutine之间切换。由于Go默认只使用一个线程,其他所有操作都必须等待插入排序完成。

为了测试这个假设,我尝试使用GOMAXPROCS=4调用程序(允许Go调度程序使用4个操作系统线程):在这种情况下,我得到了以下输出:

生成了90000个整数。
随机选择基准的快速排序花费21.1900毫秒对90000个元素进行排序
以最后一个元素为基准的快速排序花费11.4538毫秒对90000个元素进行排序
超时

正如预期的那样。(有趣的是,对于GOMAXPROCS=2,行为不确定,有时会触发超时,有时不会。我没有尝试找出为什么2个线程在这里不总是足够的原因。)

英文:

The code you posted on the Go playground gives the following output:

Generated 90000 integers.
InsertionSort        took  4465.6230ms to sort 90000 elements
QuickSortLastElement took    11.2758ms to sort 90000 elements
QuickSortRandom      took    11.6547ms to sort 90000 elements

I suspect the fact that you are not seeing the timeout being called is due to the fact that InsertionSort does make any function calls, and thus does not allow the scheduler to switch between goroutines. Since Go by default uses only a single thread, everything else has then to wait until InsertionSort has finished.

To test this hypothesis, I tried calling the program with GOMAXPROCS=4 (allowing the Go scheduler to use 4 operating system threads): In this case I get the output

Generated 90000 integers.
QuickSortRandom      took    21.1900ms to sort 90000 elements
QuickSortLastElement took    11.4538ms to sort 90000 elements
Timeout

as expected. (Curiously, for GOMAXPROCS=2 the behaviour is not deterministic, sometimes the timeout triggers and sometimes it does not. I did not try to find out why 2 threads are not always enough here.)

huangapple
  • 本文由 发表于 2015年7月27日 07:56:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/31643203.html
匿名

发表评论

匿名网友

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

确定