英文:
Need to run several func() bool and get the first false
问题
我正在尝试优化一个验证过程,该过程在一个IF语句中运行多个函数,所以我尝试使用Go的并发来解决这个问题,但是它不起作用。
我运行了以下代码,并尝试了很多不同的方法来实现通道关闭和等待组,但是我一直收到以下错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main.func1(0xc000074f60, 0x3, 0x3, 0xc000024180, 0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:24 +0x9f
main.main()
/home/raphael/projects/go/go-herupaa/main.go:25 +0xcb
goroutine 6 [chan send]:
main.a1(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:49 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
goroutine 7 [chan send]:
main.a2(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:56 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
goroutine 8 [chan send]:
main.a3(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:63 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
exit status 2
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
v := []func(chan<- bool){
a1,
a2,
a3,
}
q := make(chan bool)
c := make(chan bool)
func(c chan bool) {
for _, f := range v {
wg.Add(1)
go f(c)
}
q <- true
}(c)
p := receive(c, q)
fmt.Println(p)
wg.Wait()
}
func receive(c, q chan bool) bool {
for {
select {
case v := <-c:
if !v {
fmt.Println("Received ", v)
return false
}
case <-q:
fmt.Println("Received Exit")
return true
}
}
}
func a1(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 1")
}
func a2(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 2")
}
func a3(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 3")
}
请注意,我只返回翻译好的部分,不包括代码部分。
英文:
I'm trying to optimize a validation process which runs several functions in a IF statement, so I'm trying to use go's concurrency to solve this, but its not working
I run the following code and tried a lot of different ways to implement channel close and wait groups, but I keep receiving the following error:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main.func1(0xc000074f60, 0x3, 0x3, 0xc000024180, 0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:24 +0x9f
main.main()
/home/raphael/projects/go/go-herupaa/main.go:25 +0xcb
goroutine 6 [chan send]:
main.a1(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:49 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
goroutine 7 [chan send]:
main.a2(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:56 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
goroutine 8 [chan send]:
main.a3(0xc0000241e0)
/home/raphael/projects/go/go-herupaa/main.go:63 +0x72
created by main.main.func1
/home/raphael/projects/go/go-herupaa/main.go:22 +0x71
exit status 2
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
v := []func(chan<- bool){
a1,
a2,
a3,
}
q := make(chan bool)
c := make(chan bool)
func(c chan bool) {
for _, f := range v {
wg.Add(1)
go f(c)
}
q <- true
}(c)
p := receive(c, q)
fmt.Println(p)
wg.Wait()
}
func receive(c, q chan bool) bool {
for {
select {
case v := <-c:
if !v {
fmt.Println("Received ", v)
return false
}
case <-q:
fmt.Println("Received Exit")
return true
}
}
}
func a1(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 1")
}
func a2(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 2")
}
func a3(c chan<- bool) {
defer wg.Done()
time.Sleep(time.Millisecond * 2000)
c <- true
fmt.Println("Finish 3")
}
答案1
得分: 4
你的第一个问题出在这段代码中的 q <- true
:
func(c chan bool) {
for _, f := range v {
wg.Add(1)
go f(c)
}
q <- true
}(c)
将其封装在 func()
块中没有任何作用(我猜你之前可能在某个goroutine中运行过这段代码)。按照当前的写法,它会启动三个goroutine,然后执行 q <- true
。由于没有任何地方在接收 q
的值,这会导致阻塞。
修复这个问题后,你会遇到下一个问题:
p := receive(c, q)
fmt.Println(p)
wg.Wait()
receive
从 v
或 q
中获取值;你可能期望它从 v
中获取三个值,然后从 q
中获取一个值,但实际上它很可能(更有可能)在从 v
中获取值之前就从 q
中获取到了值(这会导致 wg.Wait
处发生死锁)。
有几种解决方法;正如评论中Peter提到的,你可以使用带缓冲的通道(例如 c := make(chan bool, len(v))
);向一个已满的通道发送数据不会阻塞。另外,我猜你的意图可能是这样的(playground):
func main() {
v := []func(chan<- bool){
a1,
a2,
a3,
}
q := make(chan bool)
c := make(chan bool)
for _, f := range v {
wg.Add(1)
go f(c)
}
// 当上述goroutine完成后,向q发送数据
go func() {
wg.Wait()
q <- true
}()
p := receive(c, q)
fmt.Println(p)
}
正如Peter指出的,你需要处理 receive
在 v
上接收到 false
并提前退出的情况。可以通过使用带缓冲的通道来解决这个问题,或者你可以启动一个仅用于丢弃在通道上接收到的数据的goroutine,例如:
case v := <-c:
if !v {
fmt.Println("Received ", v)
// 丢弃在v上接收到的其他数据,以允许其他goroutine完成
go func() { for _ = range v {}}()
return false
}
如果采用这种方法,你需要确保最终关闭 v
;实际上,你可以将其作为退出的信号,这样可以大大简化代码:
go func() {
wg.Wait()
close(c)
}()
这样就完全不需要 q
了。你的 receive
可以使用 range 迭代通道,例如 for v := range c {...
。
英文:
Your first issue is with the q <- true
in:
func(c chan bool) {
for _, f := range v {
wg.Add(1)
go f(c)
}
q <- true
}(c)
Enclosing this in a func()
block does nothing (I suspect you were running this in a go routine at some point). As written it will start three go routines and then hit q <- true
. As nothing is receiving on q
this will block.
Once you fix that you will hit the next issue:
p := receive(c, q)
fmt.Println(p)
wg.Wait()
receive
grabs something from either v
or q
; you probably expect this to get three values from v
and then one from q
but its just as likely (probably more likely) to receive from q
before v
(and that leads to another deadlock at wg.Wait
)..
There are a few ways of addressing this; as mentioned by Peter in the comments you could use buffered channels (e.g. c := make(chan bool, len(v))
); sending to a blocked channel will not block unless the channel is full. Alternatively I suspect that what you were intending is something like this (playground):
func main() {
v := []func(chan<- bool){
a1,
a2,
a3,
}
q := make(chan bool)
c := make(chan bool)
for _, f := range v {
wg.Add(1)
go f(c)
}
// When the above go routines have completed send to q
go func() {
wg.Wait()
q <- true
}()
p := receive(c, q)
fmt.Println(p)
}
As Peter points out you do need to cater for the situation where receive
receives false
on v
and exits early. This can be solved by using a buffered channel or you can spin up a go routine that just dumps data received on the channel i.e.:
case v := <-c:
if !v {
fmt.Println("Received ", v)
// Throw away anything else received on v to allow other go routines to complete
go func() { for _ = range v {}}()
return false
}
If you take this approach you need to ensure v
is closed eventually; in fact you can use that as the signal to exit which can simplify things considerably:
go func() {
wg.Wait()
close(c)
}()
This removes the need for q
completely. Your receive
can range over the channel e.g. for v := range c {...
).
答案2
得分: 1
这是对Brits的回答的补充和详细说明。
这个示例使用上下文取消来跳过写入操作,它总是排空c
。
你原始代码中的问题是你试图通过从其退出点停止流来退出,你实际上是在阻碍它。
正如Brits的回答所示,这不是停止流处理的正确方法,因为它会在管道中留下未管理的数据。
总体思路是在取消信号到达时停止向管道发送数据(关闭数据的输入源),并让管道完成对正在进行的数据的工作,完全排空它。
最后回答问题标题“需要运行多个func() bool并获取第一个false”
你可能想要实现流的缩减。捕获所有值,实现一个小逻辑尽快关闭你的缩减目标,但始终完全排空它。
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
s := make(chan os.Signal, 10)
signal.Notify(s)
<-s
cancel()
}()
// 让我们模拟取消
go func() {
<-time.After(time.Millisecond * 200)
cancel()
}()
v := []func(context.Context, chan<- bool){
a1,
a2,
a3,
}
c := make(chan bool)
var wg sync.WaitGroup
for _, f := range v {
wg.Add(1)
f := f
go func() {
f(ctx, c)
wg.Done()
}()
}
// 当上述go例程完成时发送到q
go func() {
wg.Wait()
close(c)
}()
firstRes := make(chan bool)
go func() {
var closed bool
for v := range c {
if !v && !closed {
firstRes <- v
closed = true
close(firstRes)
}
fmt.Println("Received ", v)
}
if !closed {
close(firstRes)
}
}()
var wasFalsy bool
for v := range firstRes {
wasFalsy = !v
}
fmt.Println("was falsy ", wasFalsy)
}
func a1(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- !true:
fmt.Println("Finish 1")
case <-ctx.Done():
fmt.Println("Cancelled 1")
}
}
func a2(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- true:
fmt.Println("Finish 2")
case <-ctx.Done():
fmt.Println("Cancelled 2")
}
}
func a3(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- true:
fmt.Println("Finish 3")
case <-ctx.Done():
fmt.Println("Cancelled 3")
}
}
英文:
this adds up on great and detailed answer of Brits.
This example uses context cancellation to skip writes, It always drains c
.
The problem in your original code is that you try to exit by stopping the stream from its exit point, you are litterally obstructing it.
As demonstrated in Brits answer it is not the right way to stop processing of the stream because it leaves unmanaged data in the pipe.
The general idea is to stop sending data into the pipe (close the input source of data) when the cancellation signal arrives and let the pipeline finish its job over the on going data by fully draining it.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
s := make(chan os.Signal, 10)
signal.Notify(s)
<-s
cancel()
}()
// let us simulate cancellation
go func() {
<-time.After(time.Millisecond * 200)
cancel()
}()
v := []func(context.Context, chan<- bool){
a1,
a2,
a3,
}
c := make(chan bool)
var wg sync.WaitGroup
for _, f := range v {
wg.Add(1)
f := f
go func() {
f(ctx, c)
wg.Done()
}()
}
go func() {
wg.Wait()
close(c)
}()
for v := range c {
fmt.Println("Received ", v)
}
}
func a1(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 1000)
select {
case c <- true:
fmt.Println("Finish 1")
case <-ctx.Done():
fmt.Println("Cancelled 1")
}
}
func a2(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 1000)
select {
case c <- true:
fmt.Println("Finish 2")
case <-ctx.Done():
fmt.Println("Cancelled 2")
}
}
func a3(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 1000)
select {
case c <- true:
fmt.Println("Finish 3")
case <-ctx.Done():
fmt.Println("Cancelled 3")
}
}
Finally to answer to the question title Need to run several func() bool and get the first false
You might want to implement stream reduction. capture all values, implement a small logic to close your reduction target asap. but always keep draining it completely.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
s := make(chan os.Signal, 10)
signal.Notify(s)
<-s
cancel()
}()
// let us simulate cancellation
go func() {
<-time.After(time.Millisecond * 200)
cancel()
}()
v := []func(context.Context, chan<- bool){
a1,
a2,
a3,
}
c := make(chan bool)
var wg sync.WaitGroup
for _, f := range v {
wg.Add(1)
f := f
go func() {
f(ctx, c)
wg.Done()
}()
}
// When the above go routines have completed send to q
go func() {
wg.Wait()
close(c)
}()
firstRes := make(chan bool)
go func() {
var closed bool
for v := range c {
if !v && !closed {
firstRes <- v
closed = true
close(firstRes)
}
fmt.Println("Received ", v)
}
if !closed {
close(firstRes)
}
}()
var wasFalsy bool
for v := range firstRes {
wasFalsy = !v
}
fmt.Println("was falsy ", wasFalsy)
}
func a1(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- !true:
fmt.Println("Finish 1")
case <-ctx.Done():
fmt.Println("Cancelled 1")
}
}
func a2(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- true:
fmt.Println("Finish 2")
case <-ctx.Done():
fmt.Println("Cancelled 2")
}
}
func a3(ctx context.Context, c chan<- bool) {
<-time.After(time.Millisecond * 2000)
select {
case c <- true:
fmt.Println("Finish 3")
case <-ctx.Done():
fmt.Println("Cancelled 3")
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论