【进程-线程-协程】
阅读原文时间:2021年04月22日阅读:1

# 多任务-进程/线程/协程

## 1.多任务的概念

### 1.1 并行和并发

- 单核cpu实现多任务

- 时间片轮转

- 每个任务执行很短的时间

- 假的多任务

- 并发

![image-20210316235023692](/Users/huhao/Library/Application Support/typora-user-images/image-20210316235023692.png)

- 多核cpu实现多任务

- 当运行的程序小于cpu核数

- 不用再去轮转执行

- 这就是并行

![image-20210316235227274](/Users/huhao/Library/Application Support/typora-user-images/image-20210316235227274.png)

- 一般情况下都是并发

## 2.多任务之线程

- 多线程是实现多任务比较轻便的方式

### 2.1 多线程的使用步骤

- 函数的执行

```python

import threading

def test1(g_nums):

def test2():

def main():

t2 = threading.Thread(target=test2)

t2.start()

print(threading.enumerate()) # 查看进程数量

if name == "main":

```

- 类的执行

```python

# 通过继承threading.Thread和重写run()方法可以实现类的线程执行,其原理就是继承

import threading

import time

class Mythread(threading.Thread):

if name == "main":

t = Mythread()

t.start

```

- target指定执行什么函数

- args表示传递什么参数 是一个元组

### 2.2 多线程的创建和死亡

- 线程的创建

- 调用线程threading.Thread对象是创建一个对象

- 只有开始threading.Thread.start()方法的时候线程才会创建并且开始运行

- 线程的结束

- 如果threading.Tread指定的函数执行结束,那么这个线程就结束了

- 主线程会默认等待所有子线程执行结束后它才结束

- 如果主线程不小心挂了,子线程也就挂了

### 2.3 线程之间共享全局变量

- 多任务往往配合使用

- 所以共享全局变量

- 在一个函数中对全局变量修改是否要加global要看全局变量的指向是不是改变

- 如果只是修改了数据,不用加gloabl

- 如果修改了纸箱则要加global

### 2.4 多线程的问题-共享全局变量的问题

- 多线程共享全局变量而且同时操作全局变量 有时候会出现资源竞争

![image-20210317005813818](/Users/huhao/Library/Application Support/typora-user-images/image-20210317005813818.png)

- 解决方法一:互斥锁

- 利用原子性(要么不执行要么执行完)

- 通过互斥锁来解决这个问题

- 在写操作之前加锁

- 在操作之后释放锁

```python

# 创建锁

metux = threading.lock()

# 加锁

metux.acquire()

# 释放锁

metux.release()

```

![image-20210317010822185](/Users/huhao/Library/Application Support/typora-user-images/image-20210317010822185.png)

- 解决方法二:优化 只在写操作的时候加锁子

![image-20210317011138982](/Users/huhao/Library/Application Support/typora-user-images/image-20210317011138982.png)

### 2.5 多线程的问题-多个互斥锁的死锁问题

- 你等我,我等你就会出现死锁的问题

- 如何避免死锁

- 添加超时时间

- 从程序的角度避免多个人写-银行家算法

## 3.多任务之进程

### 3.1 程序和进程

- 程序:程序就是一个没有执行的类似于xxx.exe的东西

- 进程:程序的代码+分配的资源就是进程

### 3.2 进程的状态

- 新建

- 就绪

- 运行

- 死亡

- 等待

![image-20210317131843032](/Users/huhao/Library/Application Support/typora-user-images/image-20210317131843032.png)

### 3.3 多进程的使用步骤

```python

# import threading

import multiprocessing

def test1():

def test2():

def main():

p2 = multiprocessing.Process(target=test2)

p2.start()

if name == "main":

```

### 3.4 多进程的问题

![image-20210317132749018](/Users/huhao/Library/Application Support/typora-user-images/image-20210317132749018.png)

- 多进程相当于代码copy多份+占用资源共同执行。所以进程的耗费资源很大。

- copy的信息中还有类似pid这样的信息是不一样的

- python多进程具有c语言写时拷贝的特点 只有修改的时候才会去copy

### 3.5 进程之间的通信

#### 3.5.1 socket

#### 3.5.2 queue

- 简单的队列可以在同台机器上实现进程之间的通信

- redis及其他高级队列可以跨机器

![image-20210317190019772](/Users/huhao/Library/Application Support/typora-user-images/image-20210317190019772.png)

#### 3.5.3 进程池

- 相当于先创建好进程 相当于mysql连接池

- 多个进程的时候用进程池

- 进程池可以重复利用进程池中的进程

- 可以创建几个进程要和计算机硬件相关

![image-20210317190438274](/Users/huhao/Library/Application Support/typora-user-images/image-20210317190438274.png)

## 4.进程和线程的对比

- 代码-->进程

- 进程时一坨资源和代码的总和

- qq多开时多进程

- 进程之中实现多任务的还是线程

- 线程

- 一个qq中的多个功能是线程

- 一个进程中的多个任务是线程

## 5.协程

### 5.1 迭代器

![image-20210318183103647](/Users/huhao/Library/Application Support/typora-user-images/image-20210318183103647.png)

![image-20210318183305098](/Users/huhao/Library/Application Support/typora-user-images/image-20210318183305098.png)

#### 5.1.1 可迭代对象

- 可迭代对象

- 列表、元祖、字典、集合、字符串

- 如果一个对象要是可迭代对象,那它必须有__iter__方法

```python

from collections import Iterable

isinstance([11,22,33],Iterable) # True

isinstance((11,22,33),Iterable) # True

isinstance({‘a‘:11,‘b‘:22,‘c‘:33},Iterable) # True

isinstance(‘112233‘,Iterable) # True

```

#### 5.1.2 迭代器

![image-20210318183404142](/Users/huhao/Library/Application Support/typora-user-images/image-20210318183404142.png)

- 迭代器

- 如果一个对象实现了__iter____next__方法,那它就是一个迭代器

#### 5.1.3 迭代器的应用

- 要保存数据的两种方式

- 先创造并保存数据(数据)--- 鱼

- 先生成一个迭代器(创造数据的工具)---- 鱼竿

- 斐波那契数列

- 第一种--列表

```python

nums = []

a = 0

b = 1

i = 0

while i<10:

nums.append(a)

a,b = b,a+b

i += 1

for num in nums:

print(num)

```

- 第二种--迭代器

```python

class Fibonacci(object):

fibo = Fibonacci(10)

for num in fibo:

```

### 5.2 生成器

- 生成器是特殊的迭代器

- 生成器的实现方式一:

```python

[x*2 for x in range(10)] # 迭代器

(x*2 for x in range(10)) # 生成器

```

- 函数汇总有yeild自动变成生成器

- 只要有yeild就是生成器

- 生成器的实现方式二:

```python

def create_num(all_num):

a, b =0, 1

current_num = 0

while current_num < all_num:

a, b = b, a+b

current_num += 1

obj = create_num(10)

res = next(obj)

print(res)

```

![image-20210318214702428](/Users/huhao/Library/Application Support/typora-user-images/image-20210318214702428.png)

### 5.3 协程-yield

- 通过yield实现协程-多任务

```python

import time

def task_1():

yield

def task_2():

yield # 函数中有yield相当于变成了一个生成器

def main():

t2 = task_2()

while True:

next(t2)

if name == "main":

```

### 5.4 协程-greenlet

- greenlet替换yield

```shell

sudo pip3 install greenlet

```

```python

from greenlet import greenlet

import time

def task_1():

time.sleep(1)

def task_2():

time.sleep(1)

gr1 = greenlet(test1)

gr2 = greenlet(test2)

gr1.switch()

```

### 5.5 协程-gevent

- gevent实现协程并发

```shell

sudo pip3 install gevent

```

```python

import gevent

def f(n):

g1 = gevent.spawn(f, 5)

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

g1.join()

g2.join()

g3.join()

```

- gevent的特点

- 遇到延时操作gevent就会切换任务

- gevent.sleep(0.1)

- gevent封装了greenlet

- greenlet封装了yield

- 给代码打补丁

```python

import gevent

from gevent import monkey

monkey.patch_all()

def f(n):

g1 = gevent.spawn(f, 5)

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

g1.join()

g2.join()

g3.join()

```

## 6.进程、线程、协程对比

- 进程是资源分配的最小单位

- 代码+操作系统的资源

- 线程是操作系统调度的最小单位

- 进程之间的切需要很大的资源,效率低

- 线程切换需要的资源一般,效率一般

- 协程切换任务资源很小、效率更高

- 进程和线程根据cpu核心数和任务的数量,可能是并行也可能是并发、协程一定是并发、