英文:
golang functions: parallel execution with return
问题
如何使两个函数调用f1(2)
和f1(1)
并行执行,以便整个程序执行时间为2秒而不是3秒。
package main
import (
"fmt"
"time"
)
// 等待`secs`秒
func f1(secs time.Duration) (result string) {
fmt.Printf("等待 %v\n", secs)
time.Sleep(secs * time.Second)
result = fmt.Sprintf("等待了 %d 秒", secs)
return
}
// 打印arg1和arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// 这个函数执行3秒,因为等待了很久
func runNotParallel() {
out1 := f1(2)
out2 := f1(1)
f2(out1, out2)
}
// 并行执行函数
// 待办事项:使其执行时间为2秒而不是3秒
func runParallel() {
go func() {
out1 := f1(2)
f2(out1, "")
}()
go func() {
out2 := f1(1)
f2("", out2)
}()
}
func main() {
runNotParallel()
runParallel()
}
我猜我只能用通道来实现。我应该重新定义函数f1
,还是只改变调用方式就可以了?
英文:
How to make two functions calls f1(2)
and f1(1)
execute in parallel so that all the program would execute for 2 seconds not for 3.
package main
import (
"fmt"
"time"
)
// sleeps for `secs` seconds
func f1(secs time.Duration) (result string) {
fmt.Printf("waiting %V\n", secs)
time.Sleep(secs * time.Second)
result = fmt.Sprintf("waited for %d seconds", secs)
return
}
// prints arg1, arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// this function executes for 3 seconds, because waits a lot
func runNotParallel() {
out1 := f1(2)
out2 := f1(1)
f2(out1, out2)
}
// golang parallel return functions
// todo: make it run so all the function will executes for 2 seconds not for 3
func runParallel() {
out1 := f1(2)
out2 := f1(1)
f2(out1, out2)
}
func main() {
runNotParallel()
runParallel()
}
I guess I can do it only with channels. Should I redefine function f1
or I can leave it as is and change only way I call it?
答案1
得分: 17
使用chan/goroutine
package main
import (
"fmt"
"time"
)
// 等待`secs`秒
func f1(secs time.Duration) (result string) {
fmt.Printf("等待 %v\n", secs)
time.Sleep(secs * time.Second)
result = fmt.Sprintf("等待了 %v 秒", secs)
return
}
// 打印arg1和arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// 这个函数执行3秒,因为等待了很久
func runNotParallel() {
out1 := f1(2)
out2 := f1(1)
f2(out1, out2)
}
// 并行执行的golang函数
// todo: 使其运行,使所有函数都在2秒内执行,而不是3秒
func runParallel() {
out1 := make(chan string)
out2 := make(chan string)
go func() {
out1 <- f1(2)
}()
go func() {
out2 <- f1(1)
}()
f2(<-out1, <-out2)
}
func main() {
runNotParallel()
runParallel()
}
链接:https://play.golang.org/p/G4RHiq9LJw
英文:
Use chan/goroutine
package main
import (
"fmt"
"time"
)
// sleeps for `secs` seconds
func f1(secs time.Duration) (result string) {
fmt.Printf("waiting %v\n", secs)
time.Sleep(secs * time.Second)
result = fmt.Sprintf("waited for %v seconds", secs)
return
}
// prints arg1, arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// this function executes for 3 seconds, because waits a lot
func runNotParallel() {
out1 := f1(2)
out2 := f1(1)
f2(out1, out2)
}
// golang parallel return functions
// todo: make it run so all the function will executes for 2 seconds not for 3
func runParallel() {
out1 := make(chan string)
out2 := make(chan string)
go func() {
out1 <- f1(2)
}()
go func() {
out2 <- f1(1)
}()
f2(<-out1, <-out2)
}
func main() {
runNotParallel()
runParallel()
}
答案2
得分: 9
另一种方法是使用WaitGroup
。
我编写了这个实用函数来帮助并行化一组函数:
import "sync"
// Parallelize并行化函数调用
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
所以在你的情况下,我们可以这样做:
value1 := ""
value2 := ""
func1 := func() {
value1 = f1(2)
}
func2 := func() {
value2 = f1(1)
}
Parallelize(func1, func2)
f2(out1, out2)
如果你想使用Parallelize
函数,你可以在这里找到它:https://github.com/shomali11/util
英文:
Another way you could do it is using WaitGroup
I wrote this utility function to help parallelize a group of functions:
import "sync"
// Parallelize parallelizes the function calls
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
So in your case, we could do this
value1 := ""
value2 := ""
func1 := func() {
value1 = f1(2)
}
func2 = func() {
value2 = f1(1)
}
Parallelize(func1, func2)
f2(out1, out2)
If you wanted to use the Parallelize function, you can find it here https://github.com/shomali11/util
答案3
得分: 1
这是一个没有通道但有缺失的f2同步的解决方案:
package main
import (
"fmt"
"sync"
"time"
)
// 等待`secs`秒
func f1(secs time.Duration, result *string, sg *sync.WaitGroup) {
fmt.Printf("等待 %v\n", secs)
time.Sleep(secs * time.Second)
*result = fmt.Sprintf("等待了 %d 秒", secs)
if sg != nil {
sg.Done()
}
return
}
// 打印arg1和arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// 这个函数执行3秒,因为等待了很久
func runNotParallel() {
var out1, out2 string
f1(2, &out1, nil)
f1(1, &out2, nil)
f2(out1, out2)
}
// 并行执行的golang函数
// todo: 使其运行,使所有函数都执行2秒而不是3秒
func runParallel() {
var sg sync.WaitGroup
sg.Add(2)
var out1, out2 string
go f1(2, &out1, &sg)
go f1(1, &out2, &sg)
sg.Wait()
f2(out1, out2)
}
func main() {
runNotParallel()
runParallel()
}
基本上,go操作符会阻止使用/访问返回值,但可以使用指针作为返回值的占位符在函数签名中实现。
英文:
here is a solution without channels but with the missing f2 synchronization:
package main
import (
"fmt"
"sync"
"time"
)
// sleeps for `secs` seconds
func f1(secs time.Duration, result *string, sg *sync.WaitGroup) () {
fmt.Printf("waiting %v\n", secs)
time.Sleep(secs * time.Second)
*result = fmt.Sprintf("waited for %d seconds", secs)
if sg!= nil {
sg.Done()
}
return
}
// prints arg1, arg2
func f2(arg1, arg2 string) {
fmt.Println(arg1)
fmt.Println(arg2)
}
// this function executes for 3 seconds, because waits a lot
func runNotParallel() {
var out1, out2 string
f1(2, &out1, nil)
f1(1, &out2,nil)
f2(out1, out2)
}
// golang parallel return functions
// todo: make it run so all the function will executes for 2 seconds not for 3
func runParallel() {
var sg sync.WaitGroup
sg.Add(2)
var out1, out2 string
go f1(2, &out1, &sg)
go f1(1, &out2, &sg)
sg.Wait()
f2(out1, out2)
}
func main() {
runNotParallel()
runParallel()
}
basically, go operator blocks from using/accessing a return values but it could be done using a pointers for the return place holders in the signature
答案4
得分: 0
使用Go 1.18支持泛型,channel解决方案可以变得更加易读。
func async[T any](f func() T) chan T {
ch := make(chan T)
go func() {
ch <- f()
}()
return ch
}
func main() {
startTime := time.Now().Local()
out1 := async(func() string {
time.Sleep(1 * time.Second)
return "thing 1"
})
out2 := async(func() string {
time.Sleep(2 * time.Second)
return "thing 2"
})
results := []string{<-out1, <-out2}
fmt.Printf("results: %v\n", results)
fmt.Printf("took %v", time.Since(startTime))
}
lo包提供了这个功能以及许多其他通用的辅助函数。
英文:
With go 1.18 supporting generics, the channel solution can be made even more readable.
func async[T any](f func() T) chan T {
ch := make(chan T)
go func() {
ch <- f()
}()
return ch
}
func main() {
startTime := time.Now().Local()
out1 := async(func() string {
time.Sleep(1 * time.Second)
return "thing 1"
})
out2 := async(func() string {
time.Sleep(2 * time.Second)
return "thing 2"
})
results := []string{<-out1, <-out2}
fmt.Printf("results: %v\n", results)
fmt.Printf("took %v", time.Since(startTime))
}
The lo package provides this along with lots of other generic helper functions.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论