Golang并发编程、串行编程

2018-01-26 04:54:25

线程概要

1、什么是进程,线程,多线程?

开个QQ,开了一个进程;开了迅雷,开了一个进程。
在QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。
所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作支撑的完成QQ的运行,那么这“多个工作”分别有一个线程。
所以一个进程管着多个线程。
通俗的讲:“进程是爹妈,管着众多的线程儿子”...

作者:南理汉子
链接:https://www.zhihu.com/question/25532384/answer/131906596
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

进程: 一个正在运行的程序一般就是一个进程,一个进程中可以有多个线程 线程:一条有序的CPU命令的集合体 多线程:多条有序CPU命令的集合体

备注:一个CPU在同一时刻只能执行一个CPU命令

2、一个CPU可以并发编程(多线程)吗?

例如:3个线程:(上下文切换->线程切换(时间片轮转)) 并发编程 线程1: 5个命令 线程2: 3个命令 线程3: 8个命令

线程1、线程2、线程3在一个CPU的情况下,进行并发编程,由于CPU在同一时间只能执行一个命令, 所以需要通过时间片的轮转

3、并发/并行编程、串行编程 并发编程:多个线程,会有时间片的分配问题,多个线程之间会不断的来回切换 串行编程:按照顺序一一的去执行

4、CPU有分类: CPU分为两种:物理CPU、逻辑CPU

  • 一个物理CPU可以虚拟出多个逻辑CPU(多核)
  • 8核同一时刻可以最多执行八个cup命令

5、多线程编程优缺点:
优点:分线程可以处理耗时操作,不会出现主线程阻塞 缺点:资源竞争,内存消耗,死锁

golang多线程

1、创建多线程:

  • 多线程通过 go 关键字创建分线程
  • 引入 sync 库控制多线程,var wg sync.WaitGroup 创建实例。
    • wg.Add() 方法声明分线程数。
    • wg.Wait() 方法等待声明的线程全部执行完时(线程数为0时),才会执行后续代码; 在每个分线程。
    • wg.Done() 表示该分线程执行结束,告知主线程(每当分线程执行结束,wg.Add() 声明的线程数减一)。
package main

import (  
    "fmt"
    "time"
    "sync"
)

var wg sync.WaitGroup

func main() {  
    wg.Add(2) // 添加两个线程
    go f1()
    go f2()
    wg.Wait() // 等待所有添加的线程执行结束 
}

func f1() {  
    for i:=0; i<100; i++ {
        fmt.Println("f1", i)
        time.Sleep(1 * time.Seconed) // 等待2s
    }
    wg.Done() // 当前分线程执行结束,通知主线程
}

func f2(){  
    for i:=0; i<200; i++ {
        fmt.Println("f2", i)
    }
    wg.Done() // 当前分线程执行结束,通知主线程
}

2、资源竞争、上锁

并发编程由于是CPU在多个线程建来回切换,如果多个线程对同一全局变量进行操作,则无法预测出现的结果(因为CPU在多线程间的切换是随机的)。这时就需要对数据进行上锁,即同一时刻,同一数据只能被一个线程操作。当操作时对该数据上锁,在此期间,其他线程无法操作该数据,只能等待。当该数据的操作完成时,再对其进行解锁,这样等待中的其他线程就可以操作该数据。

检测资源竞争:go run -race main.go

package main

import (  
    "fmt"
    "math/rand" // 随机数
    "time"
    "sync"
)

var wg sync.WaitGroup  
var counter int  
var mutex sync.Mutex    // 上锁、解锁

func main() {  
    wg.Add(2)
    incrementor("Foo:")
    incrementor("Bar:")
    wg.Wait()
}

func incrementor(s string) {  
    rand.Seed(time.Now().UnixNano())
    for i:=0; i<20; i++ {
        time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
        mutex.Lock() // 上锁
        counter ++ 
        fmt.Println(s, i, "counter:", counter)
        mutex.Unlock() // 解锁
    }
    wg.Done()
}
Golang数组、map、函数及类

数组/切片 数组声明: 普通声明: var arr = [5]int{} // 长度固定为5,不可扩展,{}中为初始值,默认为0 arr := [5]string{"a", "b", "c", "d"} var arr [5]int // [0, 0, 0, 0] make关键字声明数组切片: var arr = make([], 2, 4) // 长度 len(arr) 为2,容量 cap(arr) 为4,长度决定存储量,容量不够时翻倍 arr := make([], 1, 1) arr = append(arr,

Golang指针

golang中的指针与c语言指针略有不同。 1、基本类型的指针 var num int = 10 var p *int = &num // 指针的类型为所指向的变量的类型前加* 2、数组的指针与指针数组 数组的指针: arr := [3]int{1, 2, 3} var arrp *[3]int // 数组的指针 a := &arr // 直接取数组变量的地址赋值会报错(arrp = &arr) arrp = a // 通过第三个变量传递 指针数组: arr := [3]*int{} numArr := [3]int{1, 2, 3} for i, v