go基础之并发

1、Goroutines是轻量级线程

go语句在单独的线程中运行一个函数。

你可以使用go语句启动一个新的执行线程,即一个goroutine。它在一个不同的,新创建的goroutine中运行一个函数。 单个程序中的所有goroutine共享相同的地址空间。

go list.Sort() // Run list.Sort in parallel; don’t wait for it.

以下程序将打印“Hello from main goroutine”。它也可能打印出“来自另一个goroutine的Hello”,这取决于两个goroutine中的哪一个先完成。

func main() {
    go fmt.Println("Hello from another goroutine")
    fmt.Println("Hello from main goroutine")

    // At this point the program execution stops and all
    // active goroutines are killed.
}

下一个程序很可能会打印“Hello from main goroutine”和“Hello from another goroutine”。 它们可以按任何顺序打印。 另一种可能性是第二个goroutine非常慢并且在程序结束之前不打印它的消息。

func main() {
    go fmt.Println("Hello from another goroutine")
    fmt.Println("Hello from main goroutine")

    time.Sleep(time.Second) // give the other goroutine time to finish
}

这是一个更直接的例子,我们定义一个使用并发来推迟事件的函数。

// Publish prints text to stdout after the given time has expired.
// It doesn’t block but returns right away.
func Publish(text string, delay time.Duration) {
    go func() {
        time.Sleep(delay)
        fmt.Println("BREAKING NEWS:", text)
    }() // Note the parentheses. We must call the anonymous function.
}

下面是Publish函数的使用示例

func main() {
    Publish("A goroutine starts a new thread.", 5*time.Second)
    fmt.Println("Let’s hope the news will published before I leave.")

    // Wait for the news to be published.
    time.Sleep(10 * time.Second)

    fmt.Println("Ten seconds later: I’m leaving now.")
}

该程序很可能以给定的顺序打印以下三行,每行之间有五秒钟的间隔。

$ go run publish1.go
Let’s hope the news will published before I leave.
BREAKING NEWS: A goroutine starts a new thread.
Ten seconds later: I’m leaving now.

通常,不可能通过sleep来安排线程等待彼此。 Go的主要同步方法是使用channels。

Goroutines是轻量级的。会导致较小的栈增长,主要通过根据需要分配和释放堆存储。内部goroutines就像在多个操作系统线程之间复用的协程一样。如果一个goroutine被阻塞,例如等待输入,则此线程中的其他goroutine将迁移,以便它们可以继续运行。

2、channels的同步通信

channel是goroutine通过传递值来同步执行和通信的机制。

// unbuffered channel of ints
ic := make(chan int)

// buffered channel with room for 10 strings
sc := make(chan string, 10)

要在channel上发送值,请使用<-作为二元运算符。要在channel上接收值,请将其用作一元运算符。

ic <- 3   // Send 3 on the channel.
n := <-sc // Receive a string from the channel.

<- 运算符指定channel方向,发送或接收。如果没有给出方向,则通道是双向的。

chan Sushi    // can be used to send and receive values of type Sushi
chan<- string // can only be used to send strings
<-chan int    // can only be used to receive ints
  • 缓冲和无缓冲channel

    1. 如果channel的容量为零或不存在,则channel是无缓冲的,发送方将阻塞,直到接收方收到该值。
    2. 如果channel有缓冲区,则发送方仅阻塞,直到将值复制到缓冲区为止; 如果缓冲区已满,则表示等待某个接收方检索到某个值。
    3. 接收方始终阻塞,直到有数据要接收。
    4. 从nil channel发送或接收将永久阻塞。
  • 关闭channel

close函数调用后标志将不再在channel上发送值。请注意,只有在接收方期望关闭时才需要关闭channel。
1. 在调用close之后,在收到任何先前发送的值之后,接收操作将返回零值而不会阻塞。
2. 多值接收操作另外返回channel是否关闭的标识。
3. 发送或关闭已关闭的channel会导致运行时出现panic。关闭nil channel也会导致运行时panic。

ch := make(chan string)
go func() {
    ch <- "Hello!"
    close(ch)
}()

fmt.Println(<-ch) // Print "Hello!".
fmt.Println(<-ch) // Print the zero value "" without blocking.
fmt.Println(<-ch) // Once again print "".
v, ok := <-ch     // v is "", ok is false.

// Receive values from ch until closed.
for v := range ch {
    fmt.Println(v) // Will not be executed.
}

在下面的示例中,我们让Publish函数返回一个channel,该channel用于在文本发布时广播消息。

// Publish prints text to stdout after the given time has expired.
// It closes the wait channel when the text has been published.
func Publish(text string, delay time.Duration) (wait <-chan struct{}) {
    ch := make(chan struct{})
    go func() {
        time.Sleep(delay)
        fmt.Println(text)
        close(ch)
    }()
    return ch
}

请注意,我们使用空结构channel来指示channel仅用于通信,而不是用于传递数据。 下面是使用该函数例子。

wait := Publish("important news", 2 * time.Minute)
// Do some more work.
<-wait // Block until the text has been published.

猜你喜欢

转载自blog.csdn.net/hjxzb/article/details/80893807
今日推荐