Go language Brief (bottom)-Features

Source: Internet
Author: User
Tags lock queue
This is a creation in Article, where the information may have evolved or changed.

I hope you see this article is still on the bus and the subway is commuting time, I hope this article will allow you to use this time to learn a language. Of course, I hope you don't miss the station because you read my article. Oh.

If you do not know the grammar of the Go language, please take a look at the previous chapter-"Go Language Introduction (upper): Grammar"

Goroutine

Goroutine mainly uses the GO keyword to invoke functions, and you can also use anonymous functions as follows:

package mainimport "fmt"func f(msg string) {    fmt.Println(msg)}func main(){    go f("goroutine")    go func(msg string) {        fmt.Println(msg)    }("going")}

Let's look at an example, the following code contains a lot of content, including time processing, random number processing, and Goroutine code. If you are familiar with C language, you should easily understand the following code.

You can simply think of the function called by the GO keyword as pthread_create. The following code uses a for loop to create 3 threads, each using a random sleep time, and then the routine () function outputs some thread execution time information.

package mainimport "fmt"import "time"import "math/rand"func routine(name string, delay time.Duration) {    t0 := time.Now()    fmt.Println(name, " start at ", t0)    time.Sleep(delay)    t1 := time.Now()    fmt.Println(name, " end at ", t1)    fmt.Println(name, " lasted ", t1.Sub(t0))}func main() {    //生成随机种子    rand.Seed(time.Now().Unix())    var name string    for i:=0; i<3; i++{        name = fmt.Sprintf("go_%02d", i) //生成ID        //生成随机等待时间,从0-4秒        go routine(name, time.Duration(rand.Intn(5)) * time.Second)    }    //让主进程停住,不然主进程退了,goroutine也就退了    var input string    fmt.Scanln(&input)    fmt.Println("done")}

The result of the operation may be:

go_00  start at  2012-11-04 19:46:35.8974894 +0800 +0800go_01  start at  2012-11-04 19:46:35.8974894 +0800 +0800go_02  start at  2012-11-04 19:46:35.8974894 +0800 +0800go_01  end at  2012-11-04 19:46:36.8975894 +0800 +0800go_01  lasted  1.0001sgo_02  end at  2012-11-04 19:46:38.8987895 +0800 +0800go_02  lasted  3.0013001sgo_00  end at  2012-11-04 19:46:39.8978894 +0800 +0800go_00  lasted  4.0004s

Concurrency Security for Goroutine

About Goroutine, I tried it, whether it's Windows or Linux, basically using the operating system's threads. However, Goroutine has a feature, that is, if a goroutine is not blocked, then other goroutine will not be executed . This is not really concurrency, and if you want to really concurrency, you need to add the following code to the first line of your main function:

import "runtime"...runtime.GOMAXPROCS(4)

Let's take a look at an example with concurrency security issues (Note: I use C to write this go program)

This is an example that often appears in textbooks to sell tickets, I have 5 goroutine to sell tickets, the function of selling tickets selltickets is very simple, that is, a random sleep, and then the global variabletotal tickets do minus one operation.

package mainimport "fmt"import "time"import "math/rand"import "runtime"var total_tickets int32 = 10;func sell_tickets(i int){    for{        if total_tickets > 0 { //如果有票就卖            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)            total_tickets-- //卖一张票            fmt.Println("id:", i, "  ticket:", total_tickets)        }else{            break        }    }}func main() {    runtime.GOMAXPROCS(4) //我的电脑是4核处理器,所以我设置了4    rand.Seed(time.Now().Unix()) //生成随机种子    for i := 0; i < 5; i++ { //并发5个goroutine来卖票         go sell_tickets(i)    }    //等待线程执行完    var input string    fmt.Scanln(&input)    fmt.Println(total_tickets, "done") //退出时打印还有多少票}

There is no doubt that this program has concurrency security issues, so you can see the following results as you execute them:

$go run sell_tickets.goid: 0   ticket: 9  id: 0   ticket: 8  id: 4   ticket: 7  id: 1   ticket: 6  id: 3   ticket: 5  id: 0   ticket: 4  id: 3   ticket: 3  id: 2   ticket: 2  id: 0   ticket: 1  id: 3   ticket: 0  id: 1   ticket: -1  id: 4   ticket: -2  id: 2   ticket: -3  id: 0   ticket: -4  -4 done

Visible, we need to use lock, we can use mutex to solve this problem. The following code, I only list the modified content:

package mainimport "fmt"import "time"import "math/rand"import "sync"import "runtime"var total_tickets int32 = 10;var mutex = &sync.Mutex{} //可简写成:var mutex sync.Mutexfunc sell_tickets(i int){    for total_tickets>0 {        mutex.Lock()        if total_tickets > 0 {            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)            total_tickets--            fmt.Println(i, total_tickets)        }        mutex.Unlock()    }}.............

Atomic operation

Speaking of concurrency, we need to talk about atomic operations, I believe you can remember that I wrote the "No lock queue implementation," the article, which said some cas–compareandswap operation. The go language is also supported. You can take a look at the corresponding document

I'm going to give you a very simple example: The following program has 10 Goroutine, each of which adds 20 times to the CNT variable, so the final CNT should be 200. If there is no atomic atomic operation, then CNT will probably get a number less than 200.

The atomic operation is used below, so it is safe.

package mainimport "fmt"import "time"import "sync/atomic"func main() {    var cnt uint32 = 0    for i := 0; i < 10; i++ {        go func() {            for i:=0; i<20; i++ {                time.Sleep(time.Millisecond)                atomic.AddUint32(&cnt, 1)            }        }()    }    time.Sleep(time.Second)//等一秒钟等goroutine完成    cntFinal := atomic.LoadUint32(&cnt)//取数据    fmt.Println("cnt:", cntFinal)}

There are a lot of these functions, see the Atomic package documentation for GO

Channel channels

What is Channal? Channal is used to communicate, just like a Unix pipeline, in go it uses the channel.

The following program shows a routine for Goroutine and the main program communication. This program is simple enough.

package mainimport "fmt"func main() {    //创建一个string类型的channel    channel := make(chan string)    //创建一个goroutine向channel里发一个字符串    go func() { channel <- "hello" }()    msg := <- channel    fmt.Println(msg)}

Specify buffer for Channel

Specifying the size of the buffer is simple, see the following program:

package mainimport "fmt"func main() {    channel := make(chan string, 2)    go func() {        channel <- "hello"        channel <- "World"    }()    msg1 := <-channel    msg2 := <-channel    fmt.Println(msg1, msg2)}

Blocking of Channel

Note that the channel is blocked by default, that is, if the channel is full, it blocks the write, and if the channel is empty, it blocks the read. Thus, we can use this feature to synchronize our sending and receiving ends.

The following example illustrates this and the code is a bit messy, but I think it's easy to understand.

package mainimport "fmt"import "time"func main() {    channel := make(chan string) //注意: buffer为1    go func() {        channel <- "hello"        fmt.Println("write \"hello\" done!")        channel <- "World" //Reader在Sleep,这里在阻塞        fmt.Println("write \"World\" done!")        fmt.Println("Write go sleep...")        time.Sleep(3*time.Second)        channel <- "channel"        fmt.Println("write \"channel\" done!")    }()    time.Sleep(2*time.Second)    fmt.Println("Reader Wake up...")    msg := <-channel    fmt.Println("Reader: ", msg)    msg = <-channel    fmt.Println("Reader: ", msg)    msg = <-channel //Writer在Sleep,这里在阻塞    fmt.Println("Reader: ", msg)}

The result of the above code output is as follows:

Reader Wake up...Reader:  hellowrite "hello" done!write "World" done!Write go sleep...Reader:  Worldwrite "channel" done!Reader:  channel

Another benefit of this feature of channel blocking is that it allows our goroutine to block from a channel-based task at the beginning of its operation, so that it can be made into something like a thread pool. I will not write about this program. I believe you can make it yourself.

Select for multiple channel

package mainimport "time"import "fmt"func main() {    //创建两个channel - c1 c2    c1 := make(chan string)    c2 := make(chan string)    //创建两个goruntine来分别向这两个channel发送数据    go func() {        time.Sleep(time.Second * 1)        c1 <- "Hello"    }()    go func() {        time.Sleep(time.Second * 1)        c2 <- "World"    }()    //使用select来侦听两个channel    for i := 0; i < 2; i++ {        select {        case msg1 := <-c1:            fmt.Println("received", msg1)        case msg2 := <-c2:            fmt.Println("received", msg2)        }    }}

Note: The above select is blocked, so only the ugly for I <2 this kind of thing.

Channel Select blocked timeout

To solve the problem of the for loop above, there are generally two methods: one is blocking but there is timeout, and one is non-blocking. Let's see if the select is set to timeout.

for {    timeout_cnt := 0    select {    case msg1 := <-c1:        fmt.Println("msg1 received", msg1)    case msg2 := <-c2:        fmt.Println("msg2 received", msg2)    case  <-time.After(time.Second * 30):        fmt.Println("Time Out")        timout_cnt++    }    if time_cnt > 3 {        break    }}

The code highlighted in the above code is mainly used to return the Select, note the time in the case. After event.

Non-blocking of channel

OK, let's take a look at the nonblocking channel, which is also very simple, which is to add default to select, as follows:

for {    select {    case msg1 := <-c1:        fmt.Println("received", msg1)    case msg2 := <-c2:        fmt.Println("received", msg2)    default: //default会导致无阻塞        fmt.Println("nothing received!")        time.Sleep(time.Second)    }}

The closing of the channel

Close the channel can notify the other party content sent out, no longer wait. See the following routines:

package mainimport "fmt"import "time"import "math/rand"func main() {    channel := make(chan string)    rand.Seed(time.Now().Unix())    //向channel发送随机个数的message    go func () {        cnt := rand.Intn(10)        fmt.Println("message cnt :", cnt)        for i:=0; i<cnt; i++{            channel <- fmt.Sprintf("message-%2d", i)        }        close(channel) //关闭Channel    }()    var more bool = true    var msg string    for more {        select{        //channel会返回两个值,一个是内容,一个是还有没有内容        case msg, more = <- channel:            if more {                fmt.Println(msg)            }else{                fmt.Println("channel closed!")            }        }    }}

Timer

The go language can use Time.newtimer or Time.newticker to set a timer that binds to your current channel and notifies your program via the channel's blocking notification machine.

The following is an example of a timer.

package mainimport "time"import "fmt"func main() {    timer := time.NewTimer(2*time.Second)    <- timer.C    fmt.Println("timer expired!")}

The above routine looks like a sleep, yes, but the timer can stop. You need to note that the timer is only notified once. If you want a timer like C to keep you informed, you need to use ticker. The following is a ticker routine:

package mainimport "time"import "fmt"func main() {    ticker := time.NewTicker(time.Second)    for t := range ticker.C {        fmt.Println("Tick at", t)    }}

The above ticker will get you into a dead loop, and we should put it in a goroutine. The following program combines the timer and ticker

package mainimport "time"import "fmt"func main() {    ticker := time.NewTicker(time.Second)    go func () {        for t := range ticker.C {            fmt.Println(t)        }    }()    //设置一个timer,10钞后停掉ticker    timer := time.NewTimer(10*time.Second)    <- timer.C    ticker.Stop()    fmt.Println("timer expired!")}

Socket programming

Here is an echo server I try to get the socket code, it feels quite simple.

Server Side

package mainimport (    "net"    "fmt"    "io")const RECV_BUF_LEN = 1024func main() {    listener, err := net.Listen("tcp", "0.0.0.0:6666")//侦听在6666端口    if err != nil {        panic("error listening:"+err.Error())    }    fmt.Println("Starting the server")    for {        conn, err := listener.Accept() //接受连接        if err != nil {            panic("Error accept:"+err.Error())        }        fmt.Println("Accepted the Connection :", conn.RemoteAddr())        go EchoServer(conn)    }}func EchoServer(conn net.Conn) {    buf := make([]byte, RECV_BUF_LEN)    defer conn.Close()    for {        n, err := conn.Read(buf);        switch err {            case nil:                conn.Write( buf[0:n] )            case io.EOF:                fmt.Printf("Warning: End of data: %s \n", err);                return            default:                fmt.Printf("Error: Reading data : %s \n", err);                return        }     }}

Client Side

package mainimport (    "fmt"    "time"    "net")const RECV_BUF_LEN = 1024func main() {    conn,err := net.Dial("tcp", "127.0.0.1:6666")    if err != nil {        panic(err.Error())    }    defer conn.Close()    buf := make([]byte, RECV_BUF_LEN)    for i := 0; i < 5; i++ {        //准备要发送的字符串        msg := fmt.Sprintf("Hello World, %03d", i)        n, err := conn.Write([]byte(msg))        if err != nil {            println("Write Buffer Error:", err.Error())            break        }        fmt.Println(msg)        //从服务器端收字符串        n, err = conn.Read(buf)        if err !=nil {            println("Read Buffer Error:", err.Error())            break        }        fmt.Println(string(buf[0:n]))        //等一秒钟        time.Sleep(time.Second)    }}

System calls

Go language so C, so, there must be some system calls. The go language is mainly done through two packages. One is the OS package and one is the Syscall package.

Both packages are provided with unix-like system calls,

    • What the Syscall offers chroot/chmod/chmod/chdir...,getenv/getgid/getpid/getgroups/getpid/getppid ... and a lot more like inotify/ptrace/ epoll/socket/... System call.
    • The OS package does not provide much, mainly a cross-platform call. It has three sub-packages, Exec (run another command), Signal (Capture signal) and user (by UID check name, etc.)

Syscall bag of things I do not give an example, you can see the "UNIX Advanced Environment Programming" book.

In the OS, take a few examples:

Environment variables

package mainimport "os"import "strings"func main() {    os.Setenv("WEB", "http://coolshell.cn") //设置环境变量    println(os.Getenv("WEB")) //读出来    for _, env := range os.Environ() { //穷举环境变量        e := strings.Split(env, "=")        println(e[0], "=", e[1])    }}

Execute command line

The following is a simple example

package mainimport "os/exec"import "fmt"func main() {    cmd := exec.Command("ping", "127.0.0.1")    out, err := cmd.Output()    if err!=nil {        println("Command Error!", err.Error())        return    }    fmt.Println(string(out))}

A regular example of handling standard input and output is as follows:

package mainimport (    "strings"    "bytes"    "fmt"    "log"    "os/exec")func main() {    cmd := exec.Command("tr", "a-z", "A-Z")    cmd.Stdin = strings.NewReader("some input")    var out bytes.Buffer    cmd.Stdout = &out    err := cmd.Run()    if err != nil {        log.Fatal(err)    }    fmt.Printf("in all caps: %q\n", out.String())}

Command-line arguments

Processing command-line parameters in the go language is simple: (using the OS args is OK)

func main() {    args := os.Args    fmt.Println(args) //带执行文件的    fmt.Println(args[1:]) //不带执行文件的}

Under Windows, if you run the following results:

C:\Projects\Go>go run args.go aaa bbb ccc ddd[C:\Users\haoel\AppData\Local\Temp\go-build742679827\command-line-arguments\_obj\a.out.exe aaa bbb ccc ddd][aaa bbb ccc ddd]

So, what do we do if we're going to get some command-line arguments like Mysql-uroot-hlocalhost-ppwd or Cc-o3-wall-o a A.C? Go provides a package called flag that can be easily done by

package mainimport "flag"import "fmt"func main() {    //第一个参数是“参数名”,第二个是“默认值”,第三个是“说明”。返回的是指针    host := flag.String("host", "coolshell.cn", "a host name ")    port := flag.Int("port", 80, "a port number")    debug := flag.Bool("d", false, "enable/disable debug mode")    //正式开始Parse命令行参数    flag.Parse()    fmt.Println("host:", *host)    fmt.Println("port:", *port)    fmt.Println("debug:", *debug)}

The execution will look like this:

#如果没有指定参数名,则使用默认值$ go run flagtest.gohost: coolshell.cnport: 80debug: false#指定了参数名后的情况$ go run flagtest.go -host=localhost -port=22 -dhost: localhostport: 22debug: true#用法出错了(如:使用了不支持的参数,参数没有=)$ go build flagtest.go$ ./flagtest -debug -host localhost -port=22flag provided but not defined: -debugUsage of flagtest:  -d=false: enable/disable debug mode  -host="coolshell.cn": a host name  -port=80: a port numberexit status 2

It's a good feeling.

A simple HTTP Server

Code is better than words. Oh. This small program let me again back to write in C CGI time. (Go's Official document is "Writing Web applications")

Package Mainimport ("FMT" "Net/http" "Io/ioutil" "Path/filepath") const HTTP_ROOT = "/home/haoel/coolshell.cn/ "Func Main () {http. Handlefunc ("/", Roothandler) http. Handlefunc ("/view/", Viewhandler) http. Handlefunc ("/html/", Htmlhandler) http. Listenandserve (": 8080", nil)}//reads some HTTP header func Roothandler (w http. Responsewriter, R *http. Request) {fmt. fprintf (W, "Roothandler:%s\n", R.url. Path) fmt. fprintf (W, "URL:%s\n", R.url) fmt. fprintf (W, "Method:%s\n", R.method) fmt. fprintf (W, "RequestUri:%s\n", R.requesturi) fmt. fprintf (W, "Proto:%s\n", R.proto) fmt. fprintf (W, "HOST:%s\n", r.host)}//special URL handling for func viewhandler (w http. Responsewriter, R *http. Request) {fmt. fprintf (W, "Viewhandler:%s", R.url. Path)}//A service example for a static Web page. (in Http_root's HTML directory) Func Htmlhandler (w http. Responsewriter, R *http. Request) {fmt. Printf ("Htmlhandler:%s\n", R.url. Path) FileName: = Http_root + R.url. Path Fileext: = filepath. EXT (filename) content, err: = Ioutil. ReadfiLe (filename) if err! = Nil {fmt. Printf ("404 Not Found!\n") W.writeheader (http. Statusnotfound) return} var contype string switch Fileext {case ". html", "htm": Contyp E = "text/html" case ". css": contype = "Text/css" case ". js": Contype = "application/j Avascript "case". png ": Contype =" Image/png "case". jpg ",". jpeg ": Contype =" image/j Peg "case". gif ": Contype =" image/gif "Default:contype =" Text/plain "} FMT.P rintf ("ext%s, CT =%s\n", Fileext, Contype) W.header (). Set ("Content-type", Contype) fmt. fprintf (W, "%s", content)}

There are a lot of go function libraries, so let's take a look at them. I'm going to throw up another slot.--go documents are really hard to read. There are too few examples.

Let's talk so much first. This is my weekend two days learn go language learned, write too hasty, and still have some things understand not in place, and everyone please correct me!

(End of full text)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.