sync.Mutex

Go为mutual exclusion提供sync.Mutex库的两个方法

1
2
Lock
Unlock

我们可以将一块代码用Lock和Unlock包围以实现互斥运行,还可以用defer关键字来确保mutex会被释放。

练习:网页爬虫

在练习将使用Go并发特性来实现并行爬虫。修改Crawl方法并行但不重复地获取URL。
提示,可用一个Map来保存已获取的URL,但Map本身并不是线程安全的。

Read More

goroutine

goroutine是由go runtime所管理的轻量级线程。
go f(x,y,z)开启一个运行f(x,y,z)的新goroutine。f,x,y,z值的计算发生在当前goroutine,但f的执行则在新的goroutine中。

goroutine运行在同一地址空间中,所以共享内在的存取必须同步,sync包提供一些有用的基本工具,但一般你用不到它们,因为还有其它的工具。

channel

channel是一种有类型的管道,使用<-操作符就可以通过它发送和接收数值。

1
2
3
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.

参数按箭头的方向流动。和mapslice一样,channel也需要先定义再使用。
样例代码将slice中数累加,将工作分布在两个goroutine中,当二者完成时,得到最终结果。

Read More

Error

Go使用error类型的值来表示错误,这是一个内建的类似Stringer的接口。

1
2
3
type error interface {
Error() string
}

类似Stringer,fmt包也会寻找error接口来打印值。

函数经常以error作为返回类型,调用函数的代码应该通过检测error是否为nil并处理处理。一般来说error == nil说明无错误,而非nil则代表failure.

error练习

实现一个开方函数,除返回结果外,还返回error,当被操作数是负数是返回一个非nilerror,其Error()方法返回“cannot Sqrt negative number: -2”信息。
创建一个MyFloat的类type MyFloat float64,然后实现func (mf MyFloat ) Error() string使之成为一个error
这是我的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import ( "fmt"
"math")
type MyFloat float64
func (mf MyFloat) Error() string{
return fmt.Sprintf("cannot Sqrt negative number:%v.\n",float64(mf))
}
func Sqrt(f float64) (float64,error){
if f < 0 {
return 0, MyFloat(f)
}
return math.Sqrt(f),nil
}
func main(){
fmt.Println(Sqrt(4.3))
fmt.Println(Sqrt(-5.2))
}

Read More

方法method

Go没有类,但是能为类型定义方法method。所谓方法就是一个具有特殊receiver参数的函数,receiver在自己的参数列表中,位于func关键字与函数名之间。method只是一个有着receiver的特殊函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}

你也可以为非struct的数据类型定义方法,如type MyFloat float64,但方法和类型必须在同一包下。

pointer receiver

如果需要更改该类型变量的值,那么就要用到pointer receiver。如果使用值参数,那么就是值传递,其它普通函数也是一样的。

method不同于一般function之处在于其receiverpointer时,即可以接受pointer也可以接受value的变量的调用,其效果都等同于传入pointer。同理当methodreceivervalue时,不管传入pointer还是value,效果都是值传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main
import "fmt"
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(2)
ScaleFunc(&v, 10)
p := &Vertex{4, 3}
p.Scale(3)
ScaleFunc(p, 8)
fmt.Println(v, p)
}

Read More

Map

Map就是Key-value对。map必须make后才能使用,nilmap是无法赋值的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}

map literals

map literalsstruct literals是类似的,不过key是必须的。

1
2
3
4
5
6
7
8
9
10
11
12
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}

如果顶层类型只是一个类型名,可以将它从literal元素们中省略。

Read More

Go是有指针的,*T定义一个指向T类型的指针,其缺省值为nil.

1
var p *int

类似地,&操作符可以取操作数的地址。

1
2
i := 42
p = &i

*操作符也可以取指针指向的值

1
2
fmt.Println(*p) // 通过指针p读取i值
*p = 21 // 通过指针p更新i值

但与C不同的是,Go没有指针运算。

struct

struct是一组的集合

1
2
3
4
5
6
7
8
9
10
11
12
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex{1, 2})
}

struct的域可以通过.来存取。

1
2
3
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)

Read More

for循环

Go只是一种循环控制语句for,由分号;分开的三部分作为与C同,其初始化部分通常用:=形式的短声明,其作用域只在循环内。

1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}

注意与C,java等不同的是,条件部分无括号,而循环体的{}则不可省略。初始化与post循环体语句可省略,;也可以省略,如果什么都不带,则代表死循环。

1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}

死循环

1
2
3
4
5
6
package main
func main() {
for {
}
}

Read More

这只是我follow tour.golang.org时的笔记(翻译)。

Package

Go程序都由包组成,从main包开始执行。

1
2
3
4
5
6
7
8
9
10
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}

以上程序导入了matchmath/rand两个包,按约定包名与路径最后一部分相同,比如math/rand目录下的文件都以package rand的声明开头。

Import

可以将多个包用括号一起导入,如

1
2
3
4
5
6
7
8
9
10
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("Now you have %g problems.", math.Sqrt(7))
}

当然也可以用多个语句分别导入,但推荐以上方式。

Read More

Introduction

本文介绍如何使用Flask微框架在Ubuntu 15.10上搭建一个WEB应用,并使用guniconr Server来启动应用和设置Nginx作为前端反向代理。

关于WSGI的一些概念

gunicorn是一个运行于unix环境的遵循python WSGI规范的WEB Server。WSGI(web server gateway interfaces)是一种python中定义WEB应用/框架 与 WEB服务器/应用服务器之间通信规范的接口标准,旨在简化和规范上述部件间的通信,并使各部分易于替换。它定义了一组可用于其它协议之上的接口。

因此作为一种WSGI实现的gunicorn可以将HTTP请求转换为WSGI定义的python标准请求,可被同样遵循了WSGI规范的应用如django,flask接收处理,处理后的响应也同样可被逆向转换为http response并回到客户端。

nginx在此场景中则作为一个http request代理,将不uri或类型的请求发送至不同的web server处理,比如静态图片可由自己处理,而动态的数据库查询可交给flask应用来完成。

Read More

如果要使用systemd来实现开机自启动,一般将后缀为.serviceunit configuration file放置在/etc/systemd/system/下。
如果只是简单地运行一个shell脚本:

1
2
3
4
5
6
7
8
9
[Unit]
Description=run shell script
[Service]
Type=oneshot
ExecStart=/usr/bin/yourscript
[Install]
WantedBy=multi-user.target

如果还需要关闭的话,则需要加一行ExecStop,并相应修改:
(yourscript脚本接受两种参数并执行不同操作)

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Power-off gpu
[Service]
Type=oneshot
ExecStart=/usr/bin/yourscript start
ExecStop=/usr/bin/yourscript stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

当然也可以用简单地shell命令来代替unit configuration file规则

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Power-off gpu
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo OFF > /whatever/switch"
ExecStop=/bin/sh -c "echo ON > /whatever/switch"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

.service文件编写好之后,可以使用

1
systemctl enable yourservice.service

来启动服务。

如果.service文件有修改 ,可以需要使用systemctl daemon-reload命令重新加载。

更多的内置关键字和用法,可以参考https://wiki.ubuntu.com/SystemdForUpstartUsers