gpt4 book ai didi

去 channel 无限循环

转载 作者:IT王子 更新时间:2023-10-29 02:32:20 26 4
gpt4 key购买 nike

我正在尝试使用 channel 从一组 goroutine 中捕获错误,但 channel 进入无限循环,开始消耗 CPU。

func UnzipFile(f *bytes.Buffer, location string) error {
zipReader, err := zip.NewReader(bytes.NewReader(f.Bytes()), int64(f.Len()))

if err != nil {
return err
}

if err := os.MkdirAll(location, os.ModePerm); err != nil {
return err
}

errorChannel := make(chan error)
errorList := []error{}

go errorChannelWatch(errorChannel, errorList)

fileWaitGroup := &sync.WaitGroup{}

for _, file := range zipReader.File {
fileWaitGroup.Add(1)
go writeZipFileToLocal(file, location, errorChannel, fileWaitGroup)
}

fileWaitGroup.Wait()

close(errorChannel)

log.Println(errorList)

return nil
}

func errorChannelWatch(ch chan error, list []error) {
for {
select {
case err := <- ch:

list = append(list, err)
}
}
}

func writeZipFileToLocal(file *zip.File, location string, ch chan error, wg *sync.WaitGroup) {
defer wg.Done()

zipFilehandle, err := file.Open()

if err != nil {
ch <- err
return
}

defer zipFilehandle.Close()

if file.FileInfo().IsDir() {
if err := os.MkdirAll(filepath.Join(location, file.Name), os.ModePerm); err != nil {
ch <- err
}
return
}

localFileHandle, err := os.OpenFile(filepath.Join(location, file.Name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())

if err != nil {
ch <- err
return
}

defer localFileHandle.Close()

if _, err := io.Copy(localFileHandle, zipFilehandle); err != nil {
ch <- err
return
}

ch <- fmt.Errorf("Test error")
}

所以我正在循环一段文件并将它们写入我的磁盘,当出现错误时我向 errorChannel 报告将该错误保存到 slice 中。

我使用 sync.WaitGroup等待所有 goroutines 完成后我想打印 errorList并检查执行过程中是否有错误。

列表总是空的,即使我添加了ch <- fmt.Errorf("test")writeZipFileToLocal 的末尾并且 channel 总是挂掉。

我不确定我在这里遗漏了什么。

最佳答案

<强>1。对于第一点,无限循环:

引自 golang language spec :

A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

所以在这个函数中

func errorChannelWatch(ch chan error, list []error) {
for {
select {
case err := <- ch:

list = append(list, err)
}
}
}

在 ch 关闭后,这变成了一个无限循环,将 nil 值添加到 list

试试这个:

func errorChannelWatch(ch chan error, list []error) {
for err := range ch {
list = append(list, err)
}
}

<强>2。对于第二点,为什么您在错误列表中看不到任何内容:

问题是这个调用:

errorChannel := make(chan error)
errorList := []error{}

go errorChannelWatch(errorChannel, errorList)

在这里,您将 errorChannelWatch 作为值传递给 errorList。所以 slice errorList 不会被函数改变。改变的是底层数组,只要 append 调用不需要分配新数组即可。

为了补救这种情况,要么将 slice 指针传递给 errorChannelWatch,要么将其重写为对闭包的调用,捕获错误列表

对于第一个提出的解决方案,将errorChannelWatch更改为

func errorChannelWatch(ch chan error, list *[]error) {
for err := range ch {
*list = append(*list, err)
}
}

和调用

errorChannel := make(chan error)
errorList := []error{}

go errorChannelWatch(errorChannel, &errorList)

对于第二个提议的解决方案,只需将调用更改为

   errorChannel := make(chan error)
errorList := []error{}

go func() {
for err := range errorChannel {
errorList = append(errorList, err)
}
} ()

<强>3。一个小评论:

有人可能会认为,这里存在同步问题:

fileWaitGroup.Wait()

close(errorChannel)

log.Println(errorList)

您如何确定在关闭调用后 errorList 没有被修改?人们可能会认为,您不知道 goroutine errorChannelWatch 仍有多少值需要处理。

您的同步对我来说似乎是正确的,就像您执行 wg.Done()在发送到错误 channel 之后,所有错误值都会在 fileWaitGroup.Wait() 返回时发送。

但这可能会改变,如果有人后来为错误添加了缓冲 channel 或更改代码。

所以我建议至少在评论中解释同步。

关于去 channel 无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45786042/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com