> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/10.0/10.4/10.4.5.md).

# 10.4.5 指定通道的执行顺序

尽管您不应该对 goroutines 的执行顺序做任何假设，但当您需要控制这个顺序时是可以的。这小节介绍一个使用 **信号通道** 的技巧。

> 您可能会问为什么简单函数能够更容易的控制执行顺序，还要选择按顺序执行 goroutines。这个答案很简单：goroutines 能够操作并发并等待其他 goroutines 结束，而在普通的函数却不能！

说明这个主题的 Go 程序命名为 `defineOrder.go`，并分了五部分来介绍。

`defineOrder.go` 的第一部分如下：

```go
package main

import (
    "fmt"
    "time"
)

func A(a, b chan struct{}) {
    <-a
    fmt.Println("A()!")
    time.Sleep(time.Second)
    close(b)
}
```

`A()` 函数能被存储在 `a` 参数的通道中并阻塞。一旦在 `main` 函数中通道被解除阻塞，函数 `A()` 将开始工作。最后，它关闭 `b` 通道，解除在函数 `B()` 中的另个函数的阻塞。

`defineOrder.go` 的第二段代码如下：

```go
func B(a, b chan struct{}) {
    <-a
    fmt.Println("B()!")
    close(b)
}
```

`B()` 中的逻辑和函数 `A()` 相同。这个函数被阻塞直到 `a` 通道关闭。然后它做自己的工作并关闭 `b` 通道。注意 `a` 和 `b` 通道值这个函数的参数名。

`defineOrder.go` 的第三段代码如下：

```go
func C(a chan struct{}) {
    <-a
    fmt.Println("C()!")
}
```

`C()` 函数被阻塞并等待 `a` 通道关闭再开始工作。

`defineOrder.go` 的第四部分如下：

```go
func main() {
    x := make(chan struct{})
    y := make(chan struct{})
    z := make(chan struct{})
```

这三个通道作为三个函数的参数。

`defineorder.go` 的最后一段如下：

```go
    go C(z)
    go A(x, y)
    go C(z)
    go B(y, z)
    go C(z)

    close(x)
    time.Sleep(3 * time.Second)
}
```

执行 `defineOrder.go` 将产生期望的输出，即使 `C()` 函数被调用了多次：

```
$go run defineOrder.go

A()!
B()!
C()!
C()!
C()!
C()!
```

以 goroutines 调用 `C()` 函数多次会工作的很好，因为 `C()` 没有关闭任何通道。然而，如果您调用 `A()` 或 `B()` 多次的话，您可能会得到如下错误信息：

![""](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/images/chapter10/10.4.5.jpg)

如您所见，`A()` 函数被调用了俩次。然而，由于 `A()` 函数关闭了一个通道，它的 goroutines 中的一个将发现通道已经关闭并产生一个崩溃。如果您调用 `B` 多次会得到同样的崩溃信息。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://wskdsgcf.gitbook.io/mastering-go-zh-cn/10.0/10.4/10.4.5.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
