关闭完成的通道后,goroutine中缺少打印内容

时间:2019-12-29 20:07:57

标签: go channel

当我发现并不是所有的管道印刷都被打印出来时,我基于《并发中的Go》一书的示例运行以下代码。
看到“完成乘法!”丢失了。
另一方面,NumGoroutine()仅显示主要功能正在运行。
以下代码有什么问题? (https://play.golang.org/p/tkFgvKboVgS

for user in users:
    check_list.pk = None
    check_list.id = None
    check_list.user = user
    check_list.save()  # New record saved to the DB related to the user
    for task in tasks:
        task.pk = None
        task.id = None
        task.check_list = check_list
        task.save()  # New record saved to the DB related to the new check_list

输出:

AWSMobileClient.default().confirmForgotPassword(username: "my_username", newPassword: "MyNewPassword123!!", confirmationCode: "ConfirmationCode") { (forgotPasswordResult, error) in
    if let forgotPasswordResult = forgotPasswordResult {
        switch(forgotPasswordResult.forgotPasswordState) {
        case .done:
            print("Password changed successfully")
        default:
            print("Error: Could not change password.")
        }
    } else if let error = error {
        print("Error occurred: \(error.localizedDescription)")
    }
}

2 个答案:

答案 0 :(得分:2)

有些代码路径不会打印某些done消息。调度程序碰巧选择了一个不为multiply打印一个。如果您稍稍更改了代码(例如,在与现在不同的实例上登录),您会发现它也可能会丢失add done消息。 (https://play.golang.org/p/meEPM5GR9Rr)。原因如下:

如果done消息在生成器将数字写入通道后立即到达,并且乘法器读取了该消息,则乘法器将看到done可用并进行选择。 multiplier打印done消息时就是这种情况。如果在乘法器在for循环中等待时done消息到达,则乘法器将在输入通道(不是done通道)上收到关闭,导致for循环终止而不打印{ {1}}消息。

出现问题是因为您要从for循环中的通道读取,然后选择。在等待for循环从通道读取时,不会评估与选择相关的任何事件。

一种更好的处理方法是不使用for循环从通道读取数据。例如:

done

答案 1 :(得分:1)

您的addmultiply例程不是永远循环,而是for ... range循环。这样,在每个循环的顶部,它们等待下一个整数,而不是在select中等待,后者从done接收关闭或将结果发送到其流。这不是一个问题,但是这意味着如果它们的输入流被关闭,它们将返回而不会进入循环本身。

如果我add fmt.Println calls to expose the point at which they exit due to reaching the end of their input stream,行为会稍有变化(可能是由于时机所致;我并没有因为这个而烦恼,并且Burak Serdar posted his answer在我键入此字时也已)),输出变为:< / p>

add after select
2
multiply after select
generator after select
multiply after select
add after select
4
generator after select
multiply after select
add after select
6
generator after select
Closed done
done multiply !
add got end of stream - done!
finished iterating pipeline
generator after select
done generator!
ramaining goroutines: 1
finished!

通常,使生成器本身仅生成一个done信号,并使流水线函数始终写入其所有结果,使它们更可预测,这是更合理的。当然,无论谁正在读每个管道,都必须读到末尾,但是您已经在主goroutine中执行了此操作,因此我们将其传播到整个过程。 Here是您的代码的简化版本,采用这种方式;它输出:

2
generator after select
4
generator after select
6
generator after select
Closed done
8
generator after select
done generator!
multiply got end of stream - done!
add got end of stream - done!
finished iterating pipeline
remaining goroutines: 1

请注意,这一次,我们从最终生成的值(3)中获得了最终计算值(8)。