Golang中如何在一个for循环结束后重新开始循环?

Golang中如何在一个for循环结束后重新开始循环? 我有一个 main 函数

func main() {
	response, err := http.Get(geturl)
	if err != nil {
		fmt.Print(err.Error())
		os.Exit(1)
	}

	responseData, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}

	values := gjson.Get(string(responseData), "items")

	rl := ratelimit.New(3)

	prev := time.Now()

	values.ForEach(func(_, v gjson.Result) bool {
		now := rl.Take()
		url := v.Get("url").String()
		resp, err := http.Post(url, "application/x-www-form-urlencoded", nil)
		.......
		.........
		............

		return true
	})

	defer time.AfterFunc(3*time.Second, main())
}

超时后调用 main 函数不起作用… 但总的来说,为了从 geturl 获取新结果而重复 for 循环是正确的解决方案吗? 还有为什么超时后 main() 没有被调用?

另外,我可以用 goroutine 来提高 foreach 循环的性能吗?在这种情况下,goroutine 有帮助吗?


更多关于Golang中如何在一个for循环结束后重新开始循环?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

leila:

超时后调用 main 函数不起作用…

我怀疑这里发生的情况是:

  • main 函数返回。
  • 运行 main 函数的延迟(deferred)函数。
  • 另一个 goroutine 被调度等待 3 秒并重新启动 main
  • 原始的 main 调用完成,程序结束,忽略任何其他 goroutine,无论它们是否在休眠。

leila:

但是,为了从 geturl 获取新结果而重复 for 循环,这真的是正确的解决方案吗?

在我看来,你似乎是希望这个程序永远运行,并且在每次迭代之间休眠 3 秒。是这样吗?如果是的话,为什么不把整个逻辑放在一个 for {} 循环里,然后在循环底部调用 time.Sleep 来等待下一次循环开始呢?像这样使用 defer 可能会导致调用栈深度增加一两个函数,最终耗尽栈空间。而使用 for 循环则不会有这个问题。

更多关于Golang中如何在一个for循环结束后重新开始循环?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中,要在for循环结束后重新开始循环,有几种方法可以实现。你的代码中存在几个问题,特别是defer time.AfterFunc(3*time.Second, main())这行代码。

问题分析

  1. defer time.AfterFunc的问题

    • defer会在函数返回时执行,但main()函数通常不会返回
    • main()作为参数传递时会被立即执行,而不是延迟执行
  2. 重新开始循环的正确方法: 使用无限循环配合定时器

解决方案

方案1:使用无限循环和定时器

func main() {
    for {
        processData()
        time.Sleep(3 * time.Second)
    }
}

func processData() {
    response, err := http.Get(geturl)
    if err != nil {
        fmt.Print(err.Error())
        return // 返回而不是退出,以便下次循环继续
    }
    defer response.Body.Close()

    responseData, err := ioutil.ReadAll(response.Body)
    if err != nil {
        log.Println(err)
        return
    }

    values := gjson.Get(string(responseData), "items")
    
    rl := ratelimit.New(3)

    values.ForEach(func(_, v gjson.Result) bool {
        rl.Take()
        url := v.Get("url").String()
        resp, err := http.Post(url, "application/x-www-form-urlencoded", nil)
        if err != nil {
            log.Println(err)
            return true
        }
        defer resp.Body.Close()
        
        // 处理响应...
        
        return true
    })
}

方案2:使用ticker控制循环间隔

func main() {
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        processData()
    }
}

关于goroutine优化

是的,可以使用goroutine来提高foreach循环的性能,但需要注意并发控制和速率限制:

func processDataWithGoroutines() {
    response, err := http.Get(geturl)
    if err != nil {
        fmt.Print(err.Error())
        return
    }
    defer response.Body.Close()

    responseData, err := ioutil.ReadAll(response.Body)
    if err != nil {
        log.Println(err)
        return
    }

    values := gjson.Get(string(responseData), "items")
    
    // 使用带缓冲的channel控制并发
    sem := make(chan struct{}, 10) // 最大10个并发
    var wg sync.WaitGroup
    
    values.ForEach(func(_, v gjson.Result) bool {
        wg.Add(1)
        go func(v gjson.Result) {
            defer wg.Done()
            
            sem <- struct{}{}        // 获取信号量
            defer func() { <-sem }() // 释放信号量
            
            url := v.Get("url").String()
            resp, err := http.Post(url, "application/x-www-form-urlencoded", nil)
            if err != nil {
                log.Println(err)
                return
            }
            defer resp.Body.Close()
            
            // 处理响应...
            
        }(v)
        return true
    })
    
    wg.Wait() // 等待所有goroutine完成
}

完整示例代码

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"
    "time"
    
    "github.com/tidwall/gjson"
)

const geturl = "your_api_endpoint"

func main() {
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        processDataWithGoroutines()
    }
}

func processDataWithGoroutines() {
    response, err := http.Get(geturl)
    if err != nil {
        log.Printf("HTTP GET error: %v", err)
        return
    }
    defer response.Body.Close()

    responseData, err := ioutil.ReadAll(response.Body)
    if err != nil {
        log.Printf("Read response error: %v", err)
        return
    }

    values := gjson.Get(string(responseData), "items")
    
    // 控制并发数量
    sem := make(chan struct{}, 5)
    var wg sync.WaitGroup
    var mu sync.Mutex
    var successCount, errorCount int
    
    values.ForEach(func(_, v gjson.Result) bool {
        wg.Add(1)
        go func(v gjson.Result) {
            defer wg.Done()
            
            sem <- struct{}{}
            defer func() { <-sem }()
            
            url := v.Get("url").String()
            resp, err := http.Post(url, "application/x-www-form-urlencoded", nil)
            if err != nil {
                mu.Lock()
                errorCount++
                mu.Unlock()
                log.Printf("POST error for URL %s: %v", url, err)
                return
            }
            defer resp.Body.Close()
            
            mu.Lock()
            successCount++
            mu.Unlock()
            
            // 可以在这里处理响应体
            
        }(v)
        return true
    })
    
    wg.Wait()
    log.Printf("Batch completed: %d success, %d errors", successCount, errorCount)
}

这个解决方案会每3秒重新获取数据并处理,使用goroutine提高并发性能,同时通过信号量控制最大并发数避免资源耗尽。

回到顶部