go 中goroutine 的使用

一、多线程定义:

所谓的多线程,multithreading。有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。 [1] 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。

最简单的比喻多线程就像火车的每一节车厢,而进程则是火车

有些程序是一条直线,起点到终点——如简单的hello world,运行打印完,它的生命周期便结束了,像是昙花一现。

有些程序是一个圆,不断循环直到将它切断——如操作系统,一直运行直到你关机。

一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。

来个简单的例子:

package main

import (
        "fmt"
        // "reflect"
        "sync"
        "time"
)

func thread1(a int) int {
        fmt.Println("thread 1 %d", a)

        sum := 0
        for a > 0 {
                sum += a
                a--
                if a == 3 {
                        time.Sleep(2 * time.Millisecond)
                }
        }

        return sum
}

func thread2() {
        time.Sleep(1 * time.Microsecond)
        fmt.Println("thread 2")
}

var x int = 0

func main() {
        s := []int{10, 9, 8}
        var wg sync.WaitGroup
        wg.Add(1)

        time.Sleep(1 * time.Millisecond)

        go func() {
                x = thread1(s[0])
                wg.Done()
        }()
        fmt.Println(x)
        wg.Wait()
        fmt.Println(x)
        // f := false
        // str := "adfa"
        // flo := 1.1
        go thread2()

        fmt.Println("main thread")
        // time.Sleep(10 * time.Millisecond)
        fmt.Println(x)

}

  ----------------------------------------------------------------------------------

I am a newbie to golang, so please excuse if this is a very basic question. The below code gives compilation error saying 'unexpected go':

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}

I know, I can fetch the return value if call the function normally, without using goroutine. Or I can use channels etc.

My question is why is it not possible to fetch a return value like this from a goroutine.

The strict answer is that you can do that. It's just probably not a good idea. Here's code that would do that:

var x int
go func() {
    x = doSomething()
}()

This will spawn off a new goroutine which will calculate doSomething() and then assign the result to x. The problem is: how are you going to use x from the original goroutine? You probably want to make sure the spawned goroutine is done with it so that you don't have a race condition. But if you want to do that, you'll need a way to communicate with the goroutine, and if you've got a way to do that, why not just use it to send the value back?

You can add a WaitGroup to make sure you've finished and wait for it. But as you said, it's just not the way to do it, a channel is. – Not_a_GolferJan 6 '14 at 10:44

Why is it not possible to fetch a return value from a goroutine assigning it to a variable?

Run goroutine (asynchronously) and fetch return value from function are essentially controversial actions. When you say go you mean "do it asynchronously" or even simpler: "Go on! Don't wait for the function execution be finished". But when you assign function return value to a variable you are expecting to have this value within the variable. So when you do that x := go doSomething(arg)you are saying: "Go on, don't wait for the function! Wait-wait-wait! I need a returned value be accessible in x var right in the next line below!"

Channels

The most natural way to fetch a value from a goroutine is channels. Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine or in a synchronous function. You could easily obtain a value from a goroutine not breaking concurrency using select:

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        // Await both of these values
        // simultaneously, printing each one as it arrives.
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        } 
    }
}

The example is taken from Go By Example

CSP & message-passing

Go is largerly based on CSP theory. The naive description from above could be precisely outlined in terms of CSP (although I believe it is out of scope of the question). I strongly recommend to familiarize yourself with CSP theory at least because it is RAD. These short quotations give a direction of thinking:

As its name suggests, CSP allows the description of systems in terms of component processes that operate independently, and interact with each other solely through message-passing communication.

In computer science, message passing sends a message to a process and relies on the process and the supporting infrastructure to select and invoke the actual code to run. Message passing differs from conventional programming where a process, subroutine, or function is directly invoked by name.