Golang函数:并行执行并返回结果。

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

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()
}

playground

我猜我只能用通道来实现。我应该重新定义函数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()
}

playground

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 (
	&quot;fmt&quot;
	&quot;time&quot;
)

// sleeps for `secs` seconds
func f1(secs time.Duration) (result string) {
	fmt.Printf(&quot;waiting %v\n&quot;, secs)
	time.Sleep(secs * time.Second)
	result = fmt.Sprintf(&quot;waited for %v seconds&quot;, 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 &lt;- f1(2)
	}()
	go func() {
		out2 &lt;- f1(1)
	}()
	f2(&lt;-out1, &lt;-out2)
}

func main() {
	runNotParallel()
	runParallel()
}

https://play.golang.org/p/G4RHiq9LJw

答案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 &quot;sync&quot;

// 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 := &quot;&quot;
value2 := &quot;&quot;

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 (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

// sleeps for `secs` seconds
func f1(secs time.Duration, result *string, sg *sync.WaitGroup) () {
	fmt.Printf(&quot;waiting %v\n&quot;, secs)
	time.Sleep(secs * time.Second)
	*result = fmt.Sprintf(&quot;waited for %d seconds&quot;, 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, &amp;out1, nil)
	f1(1, &amp;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, &amp;out1, &amp;sg)
	go f1(1, &amp;out2, &amp;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))
}

playground

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 &lt;- f()
	}()
	return ch
}

func main() {
	startTime := time.Now().Local()

	out1 := async(func() string {
		time.Sleep(1 * time.Second)
		return &quot;thing 1&quot;
	})
	out2 := async(func() string {
		time.Sleep(2 * time.Second)
		return &quot;thing 2&quot;
	})

	results := []string{&lt;-out1, &lt;-out2}

	fmt.Printf(&quot;results: %v\n&quot;, results)
	fmt.Printf(&quot;took %v&quot;, time.Since(startTime))
}

playground

The lo package provides this along with lots of other generic helper functions.

huangapple
  • 本文由 发表于 2015年1月6日 13:04:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/27792389.html
匿名

发表评论

匿名网友

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

确定