python 进程 线程 协程

1.进程 线程 协程 概念

a.进程

我们电脑的应用程序,都是进程,假设我们用的电脑是单核的,cpu同时只能执行一个进程。当程序处于I/O阻塞的时候,CPU如果和程序一起等待,那就太浪费了,cpu会去执行其他的程序,此时就涉及到切换,切换前要保存上一个程序运行的状态,才能恢复,所以就需要有个东西来记录这个东西,就可以引出进程的概念了。
​
进程就是一个程序在一个数据集上的一次动态执行过程。进程由程序,数据集,进程控制块三部分组成。程序用来描述进程哪些功能以及如何完成;数据集是程序执行过程中所使用的资源;进程控制块用来保存程序运行的状态

b.线程

线程又叫轻量级进程,是一个基本的cpu执行单元,也是程序执行过程中的最小单元。一个进程最少也会有一个主线程,在主线程中通过threading模块,在开子线程

c.进程和线程的关系

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程
​
(2)资源分配给进程,进程是程序的主体,同一进程的所有线程共享该进程的所有资源
​
(3)cpu分配给线程,即真正在cpu上运行的是线程
​
(4)线程是最小的执行单元,进程是最小的资源管理单元

d.并行和并发

并行处理是指计算机系统中能同时执行两个或多个任务的计算方法,并行处理可同时工作于同一程序的不同方面
​
并发处理是同一时间段内有几个程序都在一个cpu中处于运行状态,但任一时刻只有一个程序在cpu上运行。
​
并发的重点在于有处理多个任务的能力,不一定要同时;而并行的重点在于就是有同时处理多个任务的能力。并行是并发的子集

以上所说的是相对于所有语言来说的,Python的特殊之处在于Python有一把GIL锁,这把锁限制了同一时间内一个进程只能有一个线程能使用cpu

2.创建线程

python 中,有关线程开发的部分被单独封装到了模块中,和线程相关的模块有以下 2 个:
_thread:是 Python 3 以前版本中 thread 模块的重命名,此模块仅提供了低级别的、原始的线程支持,以及一个简单的锁。功能比较有限。正如它的名字所暗示的(以 _ 开头),一般不建议使用 thread 模块;
threading:Python 3 之后的线程模块,提供了功能丰富的多线程支持,推荐使用。
import threading
import time
'''
# 创建线程的第一种方式:直接创建线程   (常用)
# 定义线程执行的函数
def fn(n):
    print("=========%s"%n)
    time.sleep(3)
    print("thread-1")
​
# 创建线程   args接收线程函数中的参数,参数格式是一个元组     t1是创建的子线程对象
t1 = threading.Thread(target=fn,args=('hehe',))
t1.start()  # 启动子线程
print()
print("end......")   # 主线程
'''
​
# 第二种创建线程的方式:通过继承创建线程
class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    # 重写threading.Thread中的run()方法
    def run(self):
        print("自定义线程的逻辑")
        time.sleep(3)
        print("ednding.......")
# 创建线程对象
t2 = MyThread()
t2.start()  # 子线程
print()
print("lalala")  # 主线程

3.多线程

在介绍Python中的线程之前,先明确一个问题,Python中的多线程是假的多线程! 为什么这么说,我们先明确一个概念,全局解释器锁(GIL)

3.1什么是GIL

Python代码的执行由Python虚拟机(解释器)来控制,同时只有一个线程在执行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。

3.2为什么要GIL

为了线程间数据的一致性和状态同步的完整性

3.3GIL的影响

只有一个线程在运行,无法使用多核(多个CPU)。
​
在多线程环境中,Python虚拟机按照以下方式执行。
    1.设置GIL。
    2.切换到一个线程去执行。
    3.运行。
    4.把线程设置为睡眠状态。
    5.解锁GIL。
    6.再次重复以上步骤。
​
比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。
但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。
执行一段时间后让出,多线程在Python中只能交替执行,10核也只能用到1个核

3.4多线程怎么使用多核

1、重写python编译器(官方cpython)如使用:PyPy解释器
2、调用C语言的链接库
3. 不用线程,改用进程

3.5cpu密集型(计算密集型)、I/O密集型

计算密集型就是计算、逻辑判断量非常大而且集中的类型,因为主要占用cpu资源所以又叫cpu密集型,而且当计算任务数等于cpu核心数的时候,是cpu运行效率最高的时候。   计算密集型任务由于主要消耗CPU资源,代码运行效率至关重要,C语言编写
特点:消耗cpu      比如云计算  建议使用多进程
​
IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成,99%的时间花费在IO上,脚本语言是首选,C语言最差。
​
IO密集型: Input output(一般是比较耗时的操作),  IO密集型就是磁盘的读取数据和输出数据非常大的时候就是属于IO密集型  比如网络传输、数据库等调用、爬虫等     建议使用多线程

3.6多线程类似于同时执行多个不同程序,多线程运行有如下优点:

1.使用线程可以把占据长时间的程序中的任务放到后台去处理。
2.用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
3.程序的运行速度可能加快。
4.在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

并行 并发 阻塞 非阻塞

阻塞:是指等调用结果返回之前,调用者都会进入阻塞状态等待,只有在等到结果之后再返回.
非阻塞:是指不能立即得到结果之前,该函数不会阻塞当前线程,而会立刻返回.
​
并发:是指在一个时间段内,有几个程序都在  同一个cpu  上运行,但是在任意时刻点上只有一个程序在处理机上运行.
并行:是指在一个时间段内,有几个程序都在 几个cpu 上运行,任意时刻点上,有多个程序同时运行,并且多道程序之间互不干扰.
比如要吃水饺
​
单线程: 择菜  剁馅  和面  擀皮  包饺子  烧水   煮水饺    (同步)
​
多线程(异步):  a 择菜 剁馅
             b 和面 擀皮 包饺子
             c 烧水 煮水饺  

4.创建多线程

python全局GIL解释器锁演示
'''
from threading import Thread
​
# 使用线程
def loop():
    while True:
        print("线程演示")
​
if __name__ == "__main__":
    # 创建了3个线程
    for i in range(3):
        t = Thread(target=loop)
        t.start()
'''
​
# 多进程
from multiprocessing import Process
def loop():
    while True:
        print("进程演示")
​
if __name__ == "__main__":
    for i in range(3):
        t = Process(target=loop)
        t.start()
# 多线程:是多个线程并行的技术
import threading
import time
import random
'''
# 第一种: 最原始的创建多线程的方式
# 定义一个线程函数
def fn(*args):
    time.sleep(random.uniform(1,4))
    print("子线程:",args)
​
if __name__ == "__main__":
    # 原始的方法创建多个线程
    t1 = threading.Thread(target=fn,args=("叶子楣",))
    t1.start()
    t1.join()   # 暂停阻塞  等待t1执行完后,才会跳过(不会影响t2和t3的线程)
    t2 = threading.Thread(target=fn,args=("王祖贤",))
    t2.start()
    t2.join()  # 暂停阻塞  等待t2执行完后,才会跳过(不会影响t1和t3的线程)
    t3 = threading.Thread(target=fn,args=("翁虹",))
    t3.start()
    t3.join()   # 暂停阻塞  等待t3执行完后,才会跳过(不会影响t2和t1的线程)
'''
​
# 第二种:简便的方式创建多线程
# 定义一个线程函数
def fn(*args):
    time.sleep(random.uniform(1,4))
    print("子线程:",args)
​
if __name__ == "__main__":
    # 定义一个空列表  接收创建的所有的子线程
    t_list = []
    for i in range(1,4):
        # 创建一个线程
        t = threading.Thread(target=fn)
        t.start()
        # 将创建的子线程追加到线程列表中去
        t_list.append(t)
​
    # 其他的属性和方法:(了解)
        #print(t.name,t.getName())  # 获取当前线程的名称
        #print(t.daemon)  # 守护线程  默认值是False  值为False表示该线程为非守护线程
        # print(t.ident)  # 线程号
        # print(t.isAlive()) # 查看线程是否在执行
        # print(threading.enumerate())  # 列举所有在运行的线程
        print(threading.active_count()) # 统计正在运行的线程的数量
​
    # 等到子线程执行完毕后,再执行下面的程序
    for t in t_list:
        t.join()
​

4.1线程冲突

# 在没有使用多线程之前
'''
num = 0
def add():
    global num
    for i in range(100000):
        num += 1
    print(num)
for i in range(5):
    add()
​
# 使用多线程    出现了线程冲突的现象,多个线程之间会去争抢一个资源(num)
import threading
num = 0
def add1():
    global num
    for i in range(1000000):
        num += 1
    print(num)
​
def create_thread():
    for i in range(5):
        threading.Thread(target=add1).start()
if __name__ == "__main__":
    create_thread()
'''
​
# 如何解决线程冲突呢? 通过线程锁解决
import threading
# 定义线程锁
lock = threading.Lock()
'''
# 第一种:手动添加线程锁和解开线程锁
num = 0
def add2():
    # 添加线程锁
    lock.acquire()
    global num
    for i in range(1000000):
        num += 1
    print(num)
    # 解开线程锁
    lock.release()
​
def create_thread1():
    for i in range(5):
        threading.Thread(target=add2).start()
if __name__=="__main__":
    create_thread1()
# 第一种:手动添加线程锁和解开线程锁
'''
​
# 第二种:自动添加线程锁和解开线程锁  使用with(常用)
num = 0
def add2():
    # 自动添加线程锁和解开线程锁
    with lock:
        global num
        for i in range(1000000):
            num += 1
        print(num)
​
def create_thread1():
    for i in range(5):
        threading.Thread(target=add2).start()
if __name__=="__main__":
    create_thread1()

4.2死锁

死锁:是指一个资源被多次调用,而多次调用方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
​
互相锁住对方线程需要的资源,造成死锁局面
    Thread1                   Thread2
 A(属于Thread1)             B(属于Thread2)
      B                          A

4.3线程安全

递归锁

递归锁,重用锁,用于解决死锁的问题,可重复锁
# 创建递归锁
rlock = threading.RLOCK()

4.4信号量Semaphore调度线程:控制最大并发量

5.多线程爬取当当网图书数据

 
  

https://www.cnblogs.com/aylin/p/5601969.html 博客地址

https://blog.csdn.net/sinat_38682860/article/details/80761528

你可能感兴趣的:(python,运维)