文章目录
- 用 goroutine 和通道实现并发
- 用 sync 实现并发
-
- 互斥锁
- sync.Once 结构体
- 同步等待组 zync.WaitGroup
- 竞态检测器
- 应用
-
- 自增整数生成器
- 并发消息发送器
- 多路复合计算器
- 用 select 关键字创建多通道监听器
-
- 用无缓冲通道阻塞主线程
- 用筛法求素数
- 创建随机数生成器
- 创建一个定时器
- Go Web爬虫
用 goroutine 和通道实现并发
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
go func() {
fmt.Println("开始协程")
ch <- "signal"
fmt.Println("退出协程")
}()
fmt.Println("等待协程")
<-ch
fmt.Println("完成")
}
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 6
ch <- 7
ch <- 8
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
package main
import (
"fmt"
)
func fibonacci(n int, ch chan int) {
a, b := 0, 1
for i := 0; i < n; i++ {
ch <- a
a, b = b, a+b
}
close(ch)
}
func main() {
ch := make(chan int, 6)
go fibonacci(cap(ch), ch)
for j := range ch {
fmt.Println(j)
}
}
用 sync 实现并发
互斥锁
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mutex sync.Mutex
wait := sync.WaitGroup{}
fmt.Println("Locked")
mutex.Lock()
for i := 1; i <= 5; i++ {
wait.Add(1)
go func(i int) {
fmt.Println("Not lock:", i)
mutex.Lock()
fmt.Println("Lock:", i)
time.Sleep(time.Second)
fmt.Println("Unlock:", i)
mutex.Unlock()
defer wait.Done()
}(i)
}
time.Sleep(time.Second)
fmt.Println("Unlocked")
mutex.Unlock()
wait.Wait()
}
sync.Once 结构体
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
onceBody := func() {
fmt.Println("test only once,这里只打印一次!")
}
done := make(chan bool)
for i := 0; i < 6; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 6; i++ {
<-done
}
}
同步等待组 zync.WaitGroup
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("1 goroutine sleep ...")
time.Sleep(2)
fmt.Println("1 goroutine exit ...")
}()
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("2 goroutine sleep ...")
time.Sleep(4)
fmt.Println("2 goroutine exit ...")
}()
fmt.Println("Waiting for all goroutine ")
wg.Wait()
fmt.Println("All goroutines finished!")
}
package main
import (
"fmt"
"sync"
"time"
)
func main() {
testFunc := func(wg *sync.WaitGroup, id int) {
defer wg.Done()
fmt.Printf("%v goroutine start ...\n", id)
time.Sleep(2)
fmt.Printf("%v goroutine exit ...\n", id)
}
var wg sync.WaitGroup
const N = 3
wg.Add(N)
for i := 0; i < N; i++ {
go testFunc(&wg, i)
}
fmt.Println("Waiting for all goroutine")
wg.Wait()
fmt.Println("All goroutines finished!")
}
竞态检测器
- go run/build/test -race main.go
- 模拟非法竞态访问数据
package main
import "fmt"
func main() {
c := make(chan bool)
m := make(map[string]string)
go func() {
m["a"] = "one"
c <- true
}()
m["b"] = "two"
<-c
for k, v := range m {
fmt.Println(k, v)
}
}
应用
自增整数生成器
package main
import "fmt"
func IntegerGenerator() chan int {
var ch chan int = make(chan int)
go func() {
for i := 0; ; i++ {
ch <- i
}
}()
return ch
}
func main() {
generator := IntegerGenerator()
for i := 0; i < 100; i++ {
fmt.Println(<-generator)
}
}
并发消息发送器
package main
import "fmt"
func SendNotification(user string) chan string {
notifications := make(chan string, 500)
go func() {
notifications <- fmt.Sprintf("Hi %s, welcome to our site!", user)
}()
return notifications
}
func main() {
barry := SendNotification("barry")
shirdon := SendNotification("shirdon")
fmt.Println(<-barry)
fmt.Println(<-shirdon)
}
多路复合计算器
package main
import (
"fmt"
"math/rand"
"time"
)
func doCompute(x int) int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return 1 + x
}
func branch(x int) chan int {
ch := make(chan int)
go func() {
ch <- doCompute(x)
}()
return ch
}
func Recombination(chs ...chan int) chan int {
ch := make(chan int)
for _, c := range chs {
go func(c chan int) { ch <- <-c }(c)
}
return ch
}
func main() {
result := Recombination(branch(10), branch(20), branch(30))
for i := 0; i < 3; i++ {
fmt.Println(<-result)
}
}
用 select 关键字创建多通道监听器
package main
import (
"fmt"
)
func foo(i int) chan int {
ch := make(chan int)
go func() { ch <- i }()
return ch
}
func main() {
ch1, ch2, ch3 := foo(3), foo(6), foo(9)
ch := make(chan int)
go func() {
for {
select {
case v1 := <-ch1:
ch <- v1
case v2 := <-ch2:
ch <- v2
case v3 := <-ch3:
ch <- v3
}
}
}()
for i := 0; i < 3; i++ {
fmt.Println(<-ch)
}
}
多路复合计算器
package main
import (
"fmt"
"math/rand"
"time"
)
func doCompute(x int) int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return 1 + x
}
func branch(x int) chan int {
ch := make(chan int)
go func() {
ch <- doCompute(x)
}()
return ch
}
func Recombination(branches ... chan int) chan int {
ch := make(chan int)
go func() {
for i := 0; i < len(branches); i++ {
select {
case v1 := <-branches[i]:
ch <- v1
}
}
}()
return ch
}
func main() {
result := Recombination(branch(10), branch(20), branch(30))
for i := 0; i < 3; i++ {
fmt.Println(<-result)
}
}
超时处理
timeout := time.After(1 * time.Second)
for isTimeout := false; !isTimeout; {
select {
case v1 := <-ch1:
fmt.Printf("received %d from ch1", v1)
case v2 := <-ch2:
fmt.Printf("received %d from ch2", v2)
case v3 := <-ch3:
fmt.Printf("received %d from ch3", v3)
case <-timeout:
isTimeout = true
}
}
用无缓冲通道阻塞主线程
package main
import (
"fmt"
)
func main() {
ch, quit := make(chan int), make(chan int)
go func() {
ch <- 8
quit <- 1
}()
for isQuit := false; !isQuit; {
select {
case v := <-ch:
fmt.Printf("received %d from ch", v)
case <-quit:
isQuit = true
}
}
}
用筛法求素数
package main
import "fmt"
func IntegerGenerator() chan int {
var ch chan int = make(chan int)
go func() {
for i := 2; ; i++ {
ch <- i
}
}()
return ch
}
func Filter(in chan int, number int) chan int {
out := make(chan int)
go func() {
for {
i := <-in
if i%number != 0 {
out <- i
}
}
}()
return out
}
func main() {
const max = 100
numbers := IntegerGenerator()
number := <-numbers
for number <= max {
fmt.Println(number)
numbers = Filter(numbers, number)
number = <-numbers
}
}
创建随机数生成器
package main
import "fmt"
func randGenerator() chan int {
ch := make(chan int)
go func() {
for {
select {
case ch <- 0:
case ch <- 1:
}
}
}()
return ch
}
func main() {
generator := randGenerator()
for i := 0; i < 10; i++ {
fmt.Println(<-generator)
}
}
创建一个定时器
package main
import (
"fmt"
"time"
)
func Timer(duration time.Duration) chan bool {
ch := make(chan bool)
go func() {
time.Sleep(duration)
ch <- true
}()
return ch
}
func main() {
timeout := Timer(5 * time.Second)
for {
select {
case <-timeout:
fmt.Println("already 5s!")
return
}
}
}
Go Web爬虫
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
)
func Get(url string) (result string, err error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
buf := make([]byte, 4*1024)
for {
n, err := resp.Body.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Println("文件读取完毕")
break
} else {
fmt.Println("resp.Body.Read err = ", err)
break
}
}
result += string(buf[:n])
}
return result, nil
}
func SpiderPage(i int, page chan<- int) {
url := "https://github.com/search?q=go&type=Repositories&p=1" + strconv.Itoa((i-1)*50)
fmt.Printf("正在爬取第%d个网页\n", i)
result, err := Get(url)
if err != nil {
fmt.Println("http.Get err = ", err)
return
}
filename := "page" + strconv.Itoa(i) + ".html"
f, err := os.Create(filename)
if err != nil {
fmt.Println("os.Create err = ", err)
return
}
f.WriteString(result)
f.Close()
page <- i
}
func Run(start, end int) {
fmt.Printf("正在爬取第%d页到%d页\n", start, end)
page := make(chan int)
for i := start; i <= end; i++ {
go SpiderPage(i, page)
}
for i := start; i <= end; i++ {
fmt.Printf("第%d个页面爬取完成\n", <-page)
}
}
func main() {
var start, end int
fmt.Printf("请输入起始页数字>=1:> ")
fmt.Scan(&start)
fmt.Printf("请输入结束页数字:> ")
fmt.Scan(&end)
Run(start, end)
}