我正在努力学习Go并发模式和我自己的最佳实践。
我为自己发明了一个简单的任务。
我想以一种并行的方式处理一个整数片,把它分成几个子切片,然后用一个goroutine处理每一个。
下面是我目前的解决方案,我寻求建设性的代码审查。
如果您有任何问题或需要澄清/更新,请留下评论。
// TASK:
// given the slice of integers, multiply each element by 10
// apply concurrency to solve this task by splitting slice into subslices and feeding them to goroutines
// EXAMPLE:
// input -> slice := []int{ 1, 2, 3, 4, 5, 6, 7, 8 }
// output -> slice := []int{ 10, 20, 30, 40, 50, 60, 70, 80 }
package main
import (
"fmt"
"sync"
"time"
)
func simulate_close_signal(quit chan bool, wg *sync.WaitGroup) {
defer close(quit)
defer wg.Done() // maybe I can remove this?
for {
select {
case <-time.After(1 * time.Second):
fmt.Println("Close signal sent")
return
}
}
}
func process_subslice(quit chan bool,
wg *sync.WaitGroup,
original_slice []int,
subslice_starting_index int,
subslice_ending_index int,
timeout func()) {
defer wg.Done()
for {
select {
case <-quit:
fmt.Println("Goroutine: received request to close, quiting...")
return
default:
// do I need mutex here? I think I don't since each goroutine processes independent subslice...
original_slice[subslice_starting_index] = original_slice[subslice_starting_index] * 10
subslice_starting_index++
if subslice_starting_index >= subslice_ending_index {
return
}
timeout() // added so I can test close signal
}
}
}
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8}
fmt.Println("Starting slice: ", slice)
quit := make(chan bool)
wg := sync.WaitGroup{}
for i := 0; i < 2; i++ {
wg.Add(1)
go process_subslice(quit,
&wg,
slice,
i*4, // this way slice is divided
(1+i)*4, // in two subslices with indexes [0, 3] and [4,7]
func() { time.Sleep(1 * time.Second) }) // pass empty func to test normal execution
}
wg.Add(1)
go simulate_close_signal(quit, &wg)
wg.Wait()
fmt.Println("Processed slice: ", slice)
}发布于 2021-08-09 04:36:49
看起来不错,正如您所写的,您不需要simulate_close_signal在等待组中,因为wg.Wait调用将在稍后同步其他两个写入slice的访问。
除此之外,您还可以将quit通道设置为chan struct{},因为没有任何东西正在编写或读取,因此唯一的目的是能够将其转化为close,因此您可以将其显式化。
像这样划分切片当然是可行的,尽管我确实建议您只创建一个实际的子切片,而不是传递索引,这就是它们的目的(它们都将共享基础数组,只需指向它的不同部分)。或者,如果不需要切片功能(例如,[8]int{1, 2, 3, 4, 5, 6, 7, 8}),则可以创建一个具有固定维度的实际数组。
编辑:请注意,现在我实际上是在操场上跑的。您发布的版本一致地给出了[10 20 3 4 50 60 7 8]的结果?你在发帖前试过吗?
来自quit的非阻塞读取并不像您所希望的那样工作。如果简单地打印乘法部分中的某个内容,您会注意到它两次进入这个块--这是可能发生的,因为还没有从quit通道读取任何内容。通常我会说你可以简单地放弃select,它在这里没有用。或者,您必须处理被执行一次、两次、更多甚至永远不执行的default案例!
https://codereview.stackexchange.com/questions/265766
复制相似问题