20191016

本文深入探讨了并发编程在Go语言中的应用,包括线程安全、锁机制、CSP并发模型、channel的使用及超时控制等核心概念。通过实例讲解如何避免资源竞争,实现高效并发,并介绍了生产者消费者模式的实现。

感悟

  • 之前说过前端传来的数据不可信,要进行反复校验
  • 当设计一个问题的解决方案时,一定要考虑并发,分布式的情况下,此方案还可不可行

正文

  • ubuntu18.04和docker的兼容性貌似很好
  • nginx的默认访问路径:/usr/share/nginx/html/index.html
  • nginx:alpine 最小镜像的基础
  • yum -y install nginx默认安装的是fedora版本的nginx,比较大
  • 每个docker都有自己的文件结构
  • alpine版本的nginx的默认主页也是在那个文件夹下
  • npm run build会打包项目,打包项目之后会生成dist文件,将文件拷到服务器下就可以运行
  • ssh-copy-id root@ip 下次登录就不会再要求输入密码了
  • sudo apt update
  • 直接yum安装的docker版本比较老,推荐自己进行安装
  • golang交叉编译不同平台

在这里插入图片描述

  • 拉dokcer的golang镜像在里面进行交叉编译(二段构建,没有环境的也可以build)

在这里插入图片描述

在这里插入图片描述

rander里面的html里写入js(axios),再用js(axios)请求api(drf)

  • 真nm是个人才

在这里插入图片描述
在这里插入图片描述

  • 配置安全组防火墙,9000/9000 授权对象0.0.0.0/0
  • 用docker来做服务保持
  • caddy http/2
  • 云服务器可以直接资源变更

在这里插入图片描述

在这里插入图片描述

  • docker环境变量的方式传参数

  • docker compose的yaml格式

在这里插入图片描述

Go

  • 问题,字线程还没有跑完主线程就已经挂掉,so需要在主线程加上time.Sleep(time.Second*1)
package main

import (
	"fmt"
	"time"
)

// func run(a int) {
// 	fmt.Println(a)
// }
func main() {

	for i := 1; i < 10; i++ {
		go func(a int){
			fmt.Println(a)
		}(i)
		// time.Sleep(time.Second*1)
	}
	time.Sleep(time.Second*1)
}
  • 竞争资源不是线程安全的
  • Mutex共享内存控制并发机制

共享内存加锁的并发机制

package main

import (
	"testing"
	"time"
)

func TestCounter(t *testing.T) {
	counter := 0
	for i := 0; i < 5000; i++ {
		go func() {
			counter++
		}()
	}
	time.Sleep(time.Second * 1)
	t.Logf("counter =%d", counter)
}

在这里插入图片描述

  • 要求起50000个协程,但是最终起不到50000个
func TestCounter(t *testing.T) {
	var mut sync.Mutex
	counter := 0
	for i := 0; i < 50000; i++ {
		go func() {
			defer func() {
				mut.Unlock()
			}()
			mut.Lock()
			counter++

		}()
	}
	time.Sleep(time.Second * 1)
	t.Logf("counter =%d", counter)
}
  • 此线程安全,在++之前上个锁
  • 同理此主线程也要睡1s,否则可能结果得不到预期
  • 还可以用一种更加精确专业的方法做此事
package main

import (
	"sync"
	"testing"
	// "time"
)

// 线程不安全,不好
// func TestCounter(t *testing.T) {
// 	counter := 0
// 	for i := 0; i < 5000; i++ {
// 		go func() {
// 			counter++
// 		}()
// 	}
// 	time.Sleep(time.Second * 1)
// 	t.Logf("counter =%d", counter)
// }

func TestCounter(t *testing.T) {
	var mut sync.Mutex
	var wg sync.WaitGroup
	counter := 0
	for i := 0; i < 50000; i++ {
		wg.Add(1)
		go func() {
			defer func() {
				mut.Unlock()
			}()
			mut.Lock()
			counter++
			wg.Done()
		}()
	}
	// time.Sleep(time.Second * 1)
	wg.Wait()
	t.Logf("counter =%d", counter)
}

在这里插入图片描述

  • 时间快了1s
  • 分析:add等待任务,Done告诉执行完毕的信号,wait等待会阻塞,阻塞之后才会向下执行
  • effective go effective python
  • RWLock(读写锁)在读多写少的场景操作中使用,可以加多把读锁,或一把写锁
  • sync.Mutex:互斥锁(也是全局锁,只能加一个,被锁上了需要 释放才能加下一把锁)
  • https://blog.csdn.net/fwhezfwhez/article/details/82900498
  • https://blog.csdn.net/u013210620/article/details/78357995
  • https://blog.csdn.net/tianlongtc/article/details/80067072

CSP并发模型(go中特有的)

  • 通过channel进行协调
    在这里插入图片描述

  • channel的两种模型,一个是阻塞,一个是队列的形式

  • bufferchannel是 不阻塞的,只是在最后欧多加了个容量

package channel_demo

import(
	"fmt"
	"testing"
	"time"
)

func service() string {
	time.Sleep(time.Millisecond * 50)
	return "Done"
}

func otherTask() {
	fmt.Println("working on something else")
	time.Sleep(time.Millisecond * 100)
	fmt.Println("Task is done")
}

func TestChannel(t *testing.T) {
	fmt.Println(AsyncService())
	otherTask()
}


func AsyncService() chan string{
	retCh := make(chan string)
	go func(){
		ret := service()
		fmt.Println("return result")
		retCh <- ret
		fmt.Println("service exited.")
	}()
	return retCh

}
  • 非阻塞的进行操作

在这里插入图片描述

  • 实际操作少了0.5秒

  • 加上容量编程buffer channel会在管道中非阻塞
    在这里插入图片描述

  • 输出结果发生了变化,说明是非阻塞的

多路选择超时控制
  • 用select实现超时控制
func TestChannel(t *testing.T) {
	select {
	case ret := <-AsyncService():
		t.Log(ret)
	case <- time.After(time.Millisecond*100):
		t.Error("time out")
	}
}
  • 如果超时了就会抛出错误

channel的关闭和广播

  • 生产者消费者模式中间通过buffer,在go中通过channel实现
  • 在go中有个关闭channel的操作,给出一个信号

在这里插入图片描述

任务取消

  • struct{}{} 空结构体
  • 所有的某func(xxx) 可以看成是一个工厂方法
  • close的channel是广播机制的,可以用来通道关闭的广播机制来取消协程任务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值