go-GMP模型
前言Go语言中的GMP线程模型对两级线程模型进行了一定程度的改进,它由主要三个模块构成:
模块
说明
Goroutine
轻量级的用户线程,并行的最大数量等于P的数量
Machine
一个Machine对应一个内核线程,相当于内核线程在Go语言进程中的映射,且运行过程中M和内核线程之间对应关系不会改变
Processor
承上启下的一个调度器
如下图所示:
参考小徐先生的编程世界-Golang GMP原理
线程模型
内核空间和用户空间根据资源访问权限的不同,操作系统会把内存分为内核空间和用户空间,内核空间的指令代码具备直接调度计算机底层资源的能力,比如说I/O资源等;用户空间的代码没有访问计算机底层资源的能力,需要通过系统调用等方式切换为内核态来实现对计算机底层资源的申请和调度。
用户线程和内核线程线程作为操作系统能够调度的最小单位,也分为用户线程和内核线程。(1)用户线程由用户空间的代码创建、管理和销毁,线程的调度由用户空间的线程库完成(可能是编程语言层次的线程库),无需切换内核态,资源消耗少且高效。同一进程下创建的用户线程对CPU的竞争是以进程的维度参与的,这会导致进程下的用户线程只能分时复用进程被分配的CPU时间片,所以无法很好利用CPU多核运算的优势。我们一般情况下所说的线程其实是指用户线程。(2)内核线程由操作系统管理和调度,能够直接操作计算机底层的资源,线程切换的时候CPU需要切换到内核态。它能够很好利用多核CPU计算的优势,开发人员可以通过系统调用的方式的使用内核线程。用户线程是无法被操作系统感知的,用户线程所属的进程或者内核线程才能被操作直接调度,分配CPU的使用时间。对此衍生出了 ...
go-channel
前言Go语言中实现了两种并发模型,一种是我们熟悉的线程与锁并发模型,它主要依赖于共享内存实现。另一种是Go语言中倡导使用的CSP(communicating sequential process)通信顺序进程模型。它最初由Tony Hoare于1977年的论文中被提出。
CSP类似于我们常用的同步队列,它关注的是消息传输的方式,即通道,而并不关注消息的具体发送实体和具体接受实体。同时它也极容易导致死锁,如果一个并发实体在读取一个永远没有数据放入的通道或者把数据放入一个用于不会被读取的通道中,那么它将被永远阻塞,也就是死锁。
声明12var channelName chan Tch1 := make(chan int, 1)
我们通过chan关键字声明了一个新的channel,并且声明时指定channel内传输的数据类型T。
channel的发送与接收channel作为一个队列,它会保证数据收发顺序总是遵循先入先出的原则进行,同时它也会保证在同一时刻内有且仅有一个goroutine访问channel来发送和获取数据。
写操作这段代码表示val将被发送到channel中,在channel ...
go-strings
常用函数字符串分割12sub := strings.Split(title, " ")//将字符串按空格分割//"Capitalize The Title"->["Capitalize", "The", "Title"]
字符串拼接12strings.Join(sub, " ")//["Capitalize", "The", "Title"]->"Capitalize The Title"
将字符串转为小写12s = strings.ToLower(s)//AB->ab
将字符串转为大写12s = strings.ToUpper(s)//ab->AB
将字符串首字母转为大写12s = strings.Title(s)//ab->Ab
字典(Trie)树
概念字典树也称前缀树,用边来代表字母,而从根结点到树上某一结点的路径就代表了一个字符串,是一种高效地存储和查找字符串集合的数据结构。
代码模版数据结构12345678type Trie struct { children [26]*Trie //孩子结点表示当前字符 isEnd bool //当前字符是否为结尾}func Constructor() Trie { return Trie{}}
插入1234567891011func (t *Trie) Insert(word string) { node := t for _, ch := range word { ch -= 'a' if node.children[ch] == nil { node.children[ch] = &Trie{} } node = node.children[ch] } node.isEnd = true}
查找前缀1234567891011func (t *Trie) ...
拓扑排序
模版123456789101112131415161718192021222324252627282930313233343536//初始化图的邻接表及入度//1到n共n个点,0弃用in := make([]int, n + 1)edges := make([][]int, n + 1)for i := range edges{ edges[i] = make([]int, 0)}//x,y表示x到y的一条边,构建邻接表及入度for i := 0; i < m; i++{ var x, y int fmt.Scanf("%d%d", &x, &y) edges[x] = append(edges[x], y) in[y]++ } //拓扑排序 //1.将入度为0的点加入队列 q := []int{} for i := 1; i <= n; i++{ if in[i] == 0{ q = append(q, i) } } //2.出队并且修改其余顶点的入度 for len(q) > 0{ ...
最短路
单源最短路朴素Dijkstra算法有n个结点,从1到n。
1234567891011121314151617181920212223242526272829303132333435363738394041// 构建邻接矩阵g := make([][]int, n + 1) for i := range g{ g[i] = make([]int, n + 1) } for i := 1; i <= n; i++ { for j := 1; j <= n; j++ { if i == j { g[i][j] = 0 }else{ g[i][j] = math.MaxInt32 / 2 } } } //Dijkstra算法 s := make([]bool, n + 1) //表示点是否已经加入集合 dist := make([]int, n + 1) //从源点到各个 ...
最小生成树
Prim算法Kruskal算法算法流程1234①将所有的边权重从小到大排序②枚举每条边a、b,权重c if a、b不连通 将这条边加入到集合中
map
判断key是否存在1234m := map[string]int{}if _,ok := m["a"]; ok {}