更多Go内容请见: https://blog.csdn.net/weixin_39777626/article/details/85066750
并发与并行
并发:两个或多个任务在同一时间启动、运行并结束,并且这些任务可能会互动
并行:多个任务将同时启动并执行
构建并发程序
goroutine
通道
goroutine
使用示例
goroutine.go
package main
import (
"fmt"
"time"
)
func printNumbers1() {
for i := 0; i < 10; i++ {
fmt.Printf("%d", i)
}
}
func printLetters1() {
for i := 'A'; i < 'A'+10; i++ {
fmt.Printf("%c", i)
}
}
func printNumbers2() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d", i)
}
}
func printLetters2() {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c", i)
}
}
func print1() {
printNumbers1()
printLetters1()
}
func goPrint1() {
go printNumbers1()
go printLetters1()
}
func goPrint2() {
go printNumbers2()
go printLetters2()
}
func main() {
}
goroutine_test.go
package main
import (
"testing"
"time"
)
func TestPrint1(t *testing.T) {
print1()
}
func TestGoPrint1(t *testing.T) {
goPrint1()
time.Sleep(1 * time.Millisecond)
}
func TestGoPrint2(t *testing.T) {
goPrint2()
time.Sleep(1 * time.Millisecond)
}
提高性能
影响因素
- 是否启动goroutine
- 是否延迟
- CPU数量
goroutine.go
package main
import (
//"fmt"
"time"
)
func printNumbers1() {
for i := 0; i < 10; i++ {
//fmt.Printf("%d", i)
}
}
func printLetters1() {
for i := 'A'; i < 'A'+10; i++ {
//fmt.Printf("%c", i)
}
}
func printNumbers2() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
//fmt.Printf("%d", i)
}
}
func printLetters2() {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
//fmt.Printf("%c", i)
}
}
func print1() {
printNumbers1()
printLetters1()
}
func goPrint1() {
go printNumbers1()
go printLetters1()
}
func goPrint2() {
go printNumbers2()
go printLetters2()
}
func main() {
}
goroutine_test.go
package main
import (
"testing"
"time"
)
func TestPrint1(t *testing.T) {
print1()
}
func TestGoPrint1(t *testing.T) {
goPrint1()
time.Sleep(1 * time.Millisecond)
}
func TestGoPrint2(t *testing.T) {
goPrint2()
time.Sleep(1 * time.Millisecond)
}
func BenchmarkPrint1(b *testing.B) {
for i := 0; i < b.N; i++ {
print1()
}
}
func BenchmarkGoPrint1(b *testing.B) {
for i := 0; i < b.N; i++ {
goPrint1()
}
}
func BenchmarkPrint2(b *testing.B) {
for i := 0; i < 100; i++ {
print2()
}
}
func BenchmarkGoPrint2(b *testing.B) {
for i := 0; i < 100; i++ {
goPrint2()
}
}
等待组机制
运行方式
- 声明一个等待组
- 设置等待组的计数器值
- 当一个goroutine完成任务时,计数器减一
- 阻塞,直到计数器的值变为0
goroutine.go
package main
import (
"fmt"
"sync"
"time"
)
func printNumbers2(wg *sync.WaitGroup) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
wg.Done()
}
func printLetters2(wg *sync.WaitGroup) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go printNumbers2(&wg)
go printLetters2(&wg)
wg.Wait()
}
通道
定义:带有类型的值,可以让不同的goroutine互相通信
同步
package main
import (
"fmt"
"time"
)
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true
}
func printLetters2(w chan bool) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
w <- true
}
func main() {
w1, w2 := make(chan bool), make(chan bool)
go printNumbers2(w1)
go printLetters2(w2)
<-w1
<-w2
}
消息传递
package main
import (
"fmt"
"time"
)
func thrower(c chan int) {
for i := 0; i < 5; i++ {
c <- i
fmt.Println("Threw >> ", i)
}
}
func catcher(c chan int) {
for i := 0; i < 5; i++ {
num := <-c
fmt.Println("Caught << ", num)
}
}
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true
}
func printLetters2(w chan bool) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
w <- true
}
func main() {
c := make(chan int)
go thrower(c)
go catcher(c)
time.Sleep(100 * time.Millisecond)
}
缓冲通道
package main
import (
"fmt"
"time"
)
func thrower(c chan int) {
for i := 0; i < 5; i++ {
c <- i
fmt.Println("Threw >> ", i)
}
}
func catcher(c chan int) {
for i := 0; i < 5; i++ {
num := <-c
fmt.Println("Caught << ", num)
}
}
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true
}
func printLetters2(w chan bool) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
w <- true
}
func main() {
c := make(chan int, 3)
go thrower(c)
go catcher(c)
time.Sleep(100 * time.Millisecond)
}
多通道
package main
import (
"fmt"
"time"
)
func thrower(c chan int) {
for i := 0; i < 5; i++ {
c <- i
fmt.Println("Threw >> ", i)
}
}
func catcher(c chan int) {
for i := 0; i < 5; i++ {
num := <-c
fmt.Println("Caught << ", num)
}
}
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true
}
func printLetters2(w chan bool) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
w <- true
}
func callerA(c chan string) {
c <- "hello World!"
}
func callerB(c chan string) {
c <- "Hola Mundo!"
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Microsecond)
select {
case msg := <-a:
fmt.Printf("%s from A\n", msg)
case msg := <-b:
fmt.Printf("%s from B\n", msg)
default:
fmt.Println("Default")
}
}
}
关闭通道
package main
import (
"fmt"
"time"
)
func thrower(c chan int) {
for i := 0; i < 5; i++ {
c <- i
fmt.Println("Threw >> ", i)
}
}
func catcher(c chan int) {
for i := 0; i < 5; i++ {
num := <-c
fmt.Println("Caught << ", num)
}
}
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true
}
func printLetters2(w chan bool) {
for i := 'A'; i < 'A'+10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%c ", i)
}
w <- true
}
func callerA(c chan string) {
c <- "hello World!"
close(c)
}
func callerB(c chan string) {
c <- "Hola Mundo!"
close(c)
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
var msg string
ok1, ok2 := true, true
for ok1 || ok2 {
select {
case msg, ok1 = <-a:
if ok1 {
fmt.Printf("%s from A\n", msg)
}
case msg, ok2 = <-b:
if ok2 {
fmt.Printf("%s from B\n", msg)
}
}
}
}
在Web应用中使用并发
创建马赛克图片
mosaic.go
package main
import (
"fmt"
"image"
"image/color"
"io/ioutil"
"math"
"os"
)
func averageColor(img image.Image) [3]float64 {
bounds := img.Bounds()
r, g, b := 0.0, 0.0, 0.0
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r1, g1, b1, _ := img.At(x, y).RGBA()
r, g, b = r+float64(r1), g+float64(g1), b+float64(b1)
}
}
totalPixels := float64(bounds.Max.X * bounds.Max.Y)
return [3]float64{r / totalPixels, g / totalPixels, b / totalPixels}
}
func resize(in image.Image, newWidth int) image.NRGBA {
bounds := in.Bounds()
ratio := bounds.Dx() / newWidth
out := image.NewNRGBA((image.Rect(bounds.Min.X/ratio, bounds.Min.Y/ratio, bounds.Max.X/ratio, bounds.Max.Y/ratio)))
for y, j := bounds.Min.Y, bounds.Min.Y; y < bounds.Max.Y; y, j = y+ratio,
j+1 {
for x, i := bounds.Min.X, bounds.Min.X; x < bounds.Max.X; x, i = x+ratio, i+1 {
r, g, b, a := in.At(x, y).RGBA()
out.SetNRGBA(i, j, color.NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)})
}
}
return *out
}
func tilesDB() map[string][3]float64 {
fmt.Println("Start populating tiles db ...")
db := make(map[string][3]float64)
files, _ := ioutil.ReadDir("tiles")
for _, f := range files {
name := "tiles/" + f.Name()
file, err := os.Open(name)
if err == nil {
img, _, err := image.Decode(file)
if err == nil {
db[name] = averageColor(img)
} else {
fmt.Println("error in populating TILEDB:", err, name)
}
} else {
fmt.Println("cannot open file", name, "when populating tiles db:", err)
}
file.Close()
}
fmt.Println("Finished populating tiles db.")
return db
}
func nearest(target [3]float64, db *map[string][3]float64) string {
var filename string
smallest := 1000000.0
for k, v := range *db {
dist := distance(target, v)
if dist < smallest {
filename, smallest = k, dist
}
}
delete(*db, filename)
return filename
}
func distance(p1 [3]float64, p2 [3]float64) float64 {
return math.Sqrt(sq(p2[0]-p1[0]) + sq(p2[1]-p1[1]) + sq(p2[2]-p1[2]))
}
func sq(n float64) float64 {
return n * n
}
var TILESDB map[string][3]float64
func cloneTilesDB() map[string][3]float64 {
db := make(map[string][3]float64)
for k, v := range TILESDB {
db[k] = v
}
return db
}
马赛克图片Web应用
main.go
package main
import (
"bytes"
"encoding/base64"
"fmt"
"html/template"
"image"
"image/draw"
"image/jpeg"
"net/http"
"os"
"strconv"
"time"
)
func main() {
mux := http.NewServeMux()
files := http.FileServer(http.Dir("public"))
mux.Handle("/static/", http.StripPrefix("/static/", files))
mux.HandleFunc("/", upload)
mux.HandleFunc("/mosaic", mosaic)
server := &http.Server{
Addr: "127.0.01:8080",
Handler: mux,
}
TILESDB = tilesDB()
fmt.Println("Mosaic server started.")
server.ListenAndServe()
}
func upload(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("upload.html")
t.Execute(w, nil)
}
func mosaic(w http.ResponseWriter, r *http.Request) {
t0 := time.Now()
r.ParseMultipartForm(10485760)
file, _, _ := r.FormFile("image")
defer file.Close()
tileSize, _ := strconv.Atoi(r.FormValue("tile_size"))
original, _, _ := image.Decode(file)
bounds := original.Bounds()
newimage := image.NewNRGBA(image.Rect(bounds.Min.X, bounds.Min.X, bounds.Max.X, bounds.Max.Y))
db := cloneTilesDB()
sp := image.Point{0, 0}
for y := bounds.Min.Y; y < bounds.Max.Y; y = y + tileSize {
for x := bounds.Min.X; x < bounds.Max.X; x = x + tileSize {
r, g, b, _ := original.At(x, y).RGBA()
color := [3]float64{float64(r), float64(g), float64(b)}
nearest := nearest(color, &db)
file, err := os.Open(nearest)
if err == nil {
img, _, err := image.Decode(file)
if err == nil {
t := resize(img, tileSize)
tile := t.SubImage(t.Bounds())
tileBounds := image.Rect(x, y, x+tileSize, y+tileSize)
draw.Draw(newimage, tileBounds, tile, sp, draw.Src)
} else {
fmt.Println("error", err, nearest)
}
} else {
fmt.Println("error:", nearest)
}
file.Close()
}
}
buf1 := new(bytes.Buffer)
jpeg.Encode(buf1, original, nil)
orginalStr := base64.StdEncoding.EncodeToString(buf1.Bytes())
buf2 := new(bytes.Buffer)
jpeg.Encode(buf2, newimage, nil)
mosaic := base64.StdEncoding.EncodeToString(buf2.Bytes())
t1 := time.Now()
images := map[string]string{
"original": orginalStr,
"mosaic": mosaic,
"duration": fmt.Sprintf("%v", t1.Sub(t0)),
}
t, _ := template.ParseFiles("results.html")
t.Execute(w, images)
}
result.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Mosaic</title>
...
</head>
<body>
<div class='container'>
<div class="col-md-6">
<img src="https://img-blog.csdnimg.cn/2022010620533758877.jpg" width="100%">
<div class="lead">Original</div>
</div>
<div class="col-md-6">
<img src="https://img-blog.csdnimg.cn/2022010620533766395.jpg" width="100%">
<div class="lead">Mosaic - {{ .duration }}</div>
</div>
<div class="col-md-12 center">
<a class="btn btn-lg btn-info" href="/">Go Back</a>
</div>
</div>
<br>
</body>
</html>
并发版马赛克图片生成Web应用
步骤:
- 将目标图片分割成4等份
- 同时对4张子图片进行马赛克处理
- 将4张子图片重新合并为1张马赛克图片
mosaic.go
package main
import (
"fmt"
"image"
"image/color"
"io/ioutil"
"math"
"os"
"sync"
)
func averageColor(img image.Image) [3]float64 {
bounds := img.Bounds()
r, g, b := 0.0, 0.0, 0.0
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r1, g1, b1, _ := img.At(x, y).RGBA()
r, g, b = r+float64(r1), g+float64(g1), b+float64(b1)
}
}
totalPixels := float64(bounds.Max.X * bounds.Max.Y)
return [3]float64{r / totalPixels, g / totalPixels, b / totalPixels}
}
func resize(in image.Image, newWidth int) image.NRGBA {
bounds := in.Bounds()
ratio := bounds.Dx() / newWidth
out := image.NewNRGBA((image.Rect(bounds.Min.X/ratio, bounds.Min.Y/ratio, bounds.Max.X/ratio, bounds.Max.Y/ratio)))
for y, j := bounds.Min.Y, bounds.Min.Y; y < bounds.Max.Y; y, j = y+ratio,
j+1 {
for x, i := bounds.Min.X, bounds.Min.X; x < bounds.Max.X; x, i = x+ratio, i+1 {
r, g, b, a := in.At(x, y).RGBA()
out.SetNRGBA(i, j, color.NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)})
}
}
return *out
}
func tilesDB() map[string][3]float64 {
fmt.Println("Start populating tiles db ...")
db := make(map[string][3]float64)
files, _ := ioutil.ReadDir("tiles")
for _, f := range files {
name := "tiles/" + f.Name()
file, err := os.Open(name)
if err == nil {
img, _, err := image.Decode(file)
if err == nil {
db[name] = averageColor(img)
} else {
fmt.Println("error in populating TILEDB:", err, name)
}
} else {
fmt.Println("cannot open file", name, "when populating tiles db:", err)
}
file.Close()
}
fmt.Println("Finished populating tiles db.")
return db
}
type DB struct {
mutex *sync.Mutex
store map[string][3]float64
}
func (db *DB) nearest(target [3]float64) string {
var filename string
smallest := 1000000.0
for k, v := range db.store {
dist := distance(target, v)
if dist < smallest {
filename, smallest = k, dist
}
}
delete(db.store, filename)
db.mutex.Unlock()
return filename
}
func distance(p1 [3]float64, p2 [3]float64) float64 {
return math.Sqrt(sq(p2[0]-p1[0]) + sq(p2[1]-p1[1]) + sq(p2[2]-p1[2]))
}
func sq(n float64) float64 {
return n * n
}
var TILESDB map[string][3]float64
func cloneTilesDB() DB {
db := make(map[string][3]float64)
for k, v := range TILESDB {
db[k] = v
}
tiles := DB{
store: db,
mutex: &sync.Mutex{},
}
return tiles
}
main.go
package main
import (
"bytes"
"encoding/base64"
"fmt"
"html/template"
"image"
"image/draw"
"image/jpeg"
"net/http"
"os"
"strconv"
"sync"
"time"
)
func main() {
mux := http.NewServeMux()
files := http.FileServer(http.Dir("public"))
mux.Handle("/static/", http.StripPrefix("/static/", files))
mux.HandleFunc("/", upload)
mux.HandleFunc("/mosaic", mosaic)
server := &http.Server{
Addr: "127.0.01:8080",
Handler: mux,
}
TILESDB = tilesDB()
fmt.Println("Mosaic server started.")
server.ListenAndServe()
}
func upload(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("upload.html")
t.Execute(w, nil)
}
func mosaic(w http.ResponseWriter, r *http.Request) {
t0 := time.Now()
r.ParseMultipartForm(10485760)
file, _, _ := r.FormFile("image")
defer file.Close()
tileSize, _ := strconv.Atoi(r.FormValue("tile_size"))
original, _, _ := image.Decode(file)
bounds := original.Bounds()
db := cloneTilesDB()
c1 := cut(original, &db, tileSize, bounds.Min.X, bounds.Min.Y, bounds.Max.X/2, bounds.Max.Y/2)
c2 := cut(original, &db, tileSize, bounds.Max.X/2, bounds.Min.Y, bounds.Max.X, bounds.Max.Y/2)
c3 := cut(original, &db, tileSize, bounds.Min.X, bounds.Max.Y/2, bounds.Max.X/2, bounds.Max.Y)
c4 := cut(original, &db, tileSize, bounds.Max.X/2, bounds.Max.Y/2, bounds.Max.X, bounds.Max.Y)
c := combine(bounds, c1, c2, c3, c4)
buf1 := new(bytes.Buffer)
jpeg.Encode(buf1, original, nil)
originalStr := base64.StdEncoding.EncodeToString(buf1.Bytes())
t1 := time.Now()
images := map[string]string{
"original": originalStr,
"mosaic": <-c,
"duration": fmt.Sprintf("%v", t1.Sub(t0)),
}
t, _ := template.ParseFiles("results.html")
t.Execute(w, images)
}
func cut(original image.Image, db *DB, tileSize, x1, y1, x2, y2 int) <-chan image.Image {
c := make(chan image.Image)
sp := image.Point{0, 0}
go func() {
newimage := image.NewNRGBA(image.Rect(x1, y1, x2, y2))
for y := y1; y < y2; y = y + tileSize {
for x := x1; x < x2; x = x + tileSize {
r, g, b, _ := original.At(x, y).RGBA()
color := [3]float64{float64(r), float64(g), float64(b)}
nearest := db.nearest(color)
file, err := os.Open(nearest)
if err == nil {
img, _, err := image.Decode(file)
if err == nil {
t := resize(img, tileSize)
tile := t.SubImage(t.Bounds())
tileBounds := image.Rect(x, y, x+tileSize, y+tileSize)
draw.Draw(newimage, tileBounds, tile, sp, draw.Src)
} else {
fmt.Println("error:", err)
}
} else {
fmt.Println("error:", nearest)
}
file.Close()
}
}
c <- newimage.SubImage(newimage.Rect)
}()
return c
}
func combine(r image.Rectangle, c1, c2, c3, c4 <-chan image.Image) <-chan string {
c := make(chan string)
go func() {
var wg sync.WaitGroup
img := image.NewNRGBA(r)
copy := func(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
draw.Draw(dst, r, src, sp, draw.Src)
wg.Done()
}
wg.Add(4)
var s1, s2, s3, s4 image.Image
var ok1, ok2, ok3, ok4 bool
for {
select {
case s1, ok1 = <-c1:
go copy(img, s1.Bounds(), s1, image.Point{r.Min.X, r.Min.Y})
case s2, ok2 = <-c2:
go copy(img, s2.Bounds(), s2, image.Point{r.Max.X / 2, r.Min.Y})
case s3, ok3 = <-c3:
go copy(img, s3.Bounds(), s3, image.Point{r.Min.X, r.Max.Y / 2})
case s4, ok4 = <-c4:
go copy(img, s4.Bounds(), s4, image.Point{r.Max.X / 2, r.Max.Y / 2})
}
if ok1 && ok2 && ok3 && ok4 {
break
}
}
wg.Wait()
buf2 := new(bytes.Buffer)
jpeg.Encode(buf2, img, nil)
c <- base64.StdEncoding.EncodeToString(buf2.Bytes())
}()
return c
}
更多Go内容请见: https://blog.csdn.net/weixin_39777626/article/details/85066750