From 2b4ff6c8fb16ab74a1fbc79f768f2d0c84dcb96e Mon Sep 17 00:00:00 2001 From: spawnris Date: Sun, 17 Aug 2014 23:49:40 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=AC=AC=E4=B9=9D=E7=AB=A0=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E8=8A=82,=E9=94=81=E5=92=8Csync=E5=8C=85=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E7=BF=BB=E8=AF=91=E3=80=8009.3.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/09.3.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 eBook/09.3.md diff --git a/eBook/09.3.md b/eBook/09.3.md new file mode 100644 index 0000000..9577fa9 --- /dev/null +++ b/eBook/09.3.md @@ -0,0 +1,32 @@ +#9.3锁和sync包 +在一些复杂的程序中,通常通过不同线程执行不同应用来实现程序的并发。当不同线程要使用同一个变量时,经常会出现一个问题:无法预知变量被不同线程修改的顺序!(这通常被称为资源竞争,指不同线程对同一变量使用的竞争。)显然这无法让人容忍,那我们该如何解决这个问题呢? + +经典的做法是一次只能让一个线程对共享变量进行操作。当变量被一个线程改变时(临界区),我们为它上锁,直到这个线程执行完成并解锁后,其他线程才能访问它。 + +特别是我们之前章节学习的map类型是不存在锁的机制来实现这种效果(出于对性能的考虑),所以map类型是非线程安全的.当并行访问一个共享的map类型的数据,map数据将会出错。 + +在Go语言中这种锁的机制是通过sync包中Mutex来实现的。sync来源于"synchronized"一词,这意味着线程将有序的对同一变量进行访问。 + +sync.Mutex是一个互斥锁,它的作用是守护在临界区入口来确保同一时间只能有一个线程进入临界区。 + +假设info是一个需要上锁的放在共享内存中的变量。通过包含Mutex来实现的一个典型例子如下: + + import “sync” type Info struct { mu sync.Mutex // ... other fields, e.g.: Str string } 如果一个函数想要改变这个变量可以这样写: + + func Update(info *Info) { + info.mu.Lock() // critical section: info.Str = // new value // end critical section info.mu.Unlock() } 还有一个很有用的例子是通过Mutex来实现一个可以上锁的共享缓冲器: + type SyncedBuffer struct { + lock sync.Mutex + buffer bytes.Buffer + } + +在sync包中还有一个RWMutex锁:他能通过RLock()来允许同一时间多个线程对变量进行读操作,但是只能一个线程进行写操作。如果使用Lock()将和普通的Mutex作用相同。包中还有一个方便的Once类型变量的方法once.Do(call),这个方法确保被调用函数只能被调用一次。 + +相对简单的情况下,通过使用sync包可以解决同一时间只能一个线程访问变量或map类型数据的问题。如果这种方式导致程序明显变慢或者引起其他问题,我们要重新思考来通过goroutines和channels来解决问题,这是在Go语言中所提倡用来实现并发的技术。我们将在第14章对其深入了解,并在14.7的章节中对这两种方式进行比较。 + + + +##链接 +- [目录](directory.md) +- 上一节:[regexp包](09.2.md) +- 下一节:[精密计算和big包](09.3.md) \ No newline at end of file From e463d93a131fbcc8970461d3561fe7f18a59db93 Mon Sep 17 00:00:00 2001 From: spawnris Date: Sun, 17 Aug 2014 23:50:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E7=AC=AC=E4=B9=9D=E7=AB=A0=E7=AC=AC?= =?UTF-8?q?=E5=9B=9B=E8=8A=82,=E7=B2=BE=E5=AF=86=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E5=92=8Cbig=E5=8C=85=E7=9A=84=E5=86=85=E5=AE=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E3=80=8009.4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/09.4.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 eBook/09.4.md diff --git a/eBook/09.4.md b/eBook/09.4.md new file mode 100644 index 0000000..0c284a8 --- /dev/null +++ b/eBook/09.4.md @@ -0,0 +1,13 @@ +#精密计算和big包 +我们知道有些时候通过编程的方式去进行计算是不精确的。如果你使用Go语言中的fload64类型进行浮点运算,返回结果将精确到15位,足以满足大多数的任务。当对超出in64或者uint64类型这样的大数进行计算时,如果对精度没有要求,float32或者float64可以胜任,但如果对精度有严格要求的时候,我们不能使用浮点数,在内存中它们只能被近似的表示。 + +对于整数的高精度计算Go语言中提供了big包。其中包含了math包:有用来表示大整数的big.Int和表示大有理数的big.Rat类型(可以表示为2/5或3.1416这样的分数,而不是无理数或π)。这些类型可以实现任意位类型的数字,只要内存足够大。缺点是更大的内存和处理开销使它们使用起来要比内置的数字类型慢很多。 + +大的整型数字是通过big.NewInt(n)来构造的,其中n位int64类型整数。而大有理数是用过big.NewRat(N,D)方法构造.N(分子)和D(分母)都是int64型整数。因为Go语言不支持运算符重载,所以所有大数字类型都有像是Add()和Mul()这样的方法。它们作用于作为receiver的整数和有理数,大多数情况下它们修改receiver并以receiver作为返回结果。因为没有必要创建big.Int类型的临时变量来存放中间结果,所以这样的运算可通过内存链式存储。 + +示例 9.2 [big.go](exmaples/chapter_9/big.go) + + package main import ( "fmt" "math"" "math/big" ) func main() { // Here are some calculations with bigInts: im := big.NewInt(math.MaxInt64) in := im io := big.NewInt(1956) ip := big.NewInt(1) ip.Mul(im, in).Add(ip, im).Div(ip, io) fmt.Printf(“Big Int: %v\n”, ip) // Here are some calculations with bigInts: rm := big.NewRat(math.MaxInt64, 1956) rn := big.NewRat(-1956, math.MaxInt64) ro := big.NewRat(19, 56) rp := big.NewRat(1111, 2222) rq := big.NewRat(1, 1) rq.Mul(rm, rn).Add(rq, ro).Mul(rq, rp) fmt.Printf(“Big Rat: %v\n”, rq) } + 输出结果: + + Big Int: 43492122561469640008497075573153004 Big Rat: -37/112 \ No newline at end of file