本章内容

  1. 线程
  2. 进程
  3. 协程

线程是最小的调度单位

进程是最小的管理单元

线程

多线程的特点:

  1. 线程的并发是利用cpu上下文切换
  2. 多线程的执行的顺序是无序的
  3. 多线程共享全局变量
  4. 线程是继承在进程里的,没有进程就没有线程
  5. GIL全局解释器锁
  6. 只有在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适

多线程的好处

  1. 达到充分利用CPU的目的。多线程完成cpu内核的快速切换,提高CPU的利用率
  2. 多线程可以防止数据阻塞问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
  3. 多线程可以提高系统I/O问题的处理效率等

并行和并发

  • 并行:指在同一时刻,有多条指令在多个处理器上同时执行;
  • 并发:指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
#并发

import time ,threading
def test1(n):
for i in range(n):
time.sleep(1)
print('task1',i) def test2(n):
for i in range(n):
time.sleep(1)
print('task2',i) thread1 = threading.Thread(target=test1,args=(10,))
thread2 = threading.Thread(target=test2,args=(10,))
thread1.start()
thread2.start() ....................................... #运行共用10s
#无序

import time,threading
def test1(n):
time.sleep(1)
print('task',n) for i in range(10):
thread1 = threading.Thread(target=test1,args=(i,))
thread1.start() ...............................运行结果
task 1
task 2
task 3
task 7
task 5
task 4
task 6
task 0
task 9
task 8
#共享全局变量

import threading
num=0
def test1():
global num
for i in range(10):
num+=1 def test2():
global num
print(num) thread1 = threading.Thread(target=test1)
thread2 = threading.Thread(target=test2)
thread1.start()
thread2.start() ..............................................运行结果 10
#GIL全局解释器锁实例

import threading
global_num = 0 def test1():
global global_num
for i in range(1000000):
global_num += 1 def test2():
global global_num
for i in range(1000000):
global_num += 1
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
print(global_num) ......................................运行结果
109704 #运行结果本应该为2000000,为什么运行结果远小于应得结果?

第一个原因:运行代码后加上我们启动的两个线程,cpu共起了3个线程;其中一个用于执行代码(取名a),两个用于执行函数test1和test2(t1 t2);a线程运行到print(global_num)时,test1和test2循环还没有执行完毕!线程a就已经输出了;所以得到的值不是2000000

解决方法:让a线程等test1和test2循环完成之后再执行print();

#原因1解决后

import threading
global_num = 0 def test1():
global global_num
for i in range(1000000):
global_num += 1 def test2():
global global_num
for i in range(1000000):
global_num += 1
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
print(global_num) ...........................................运行结果
1240490 #为什么还是不等于2000000呢?

第二个原因:GIL全局解释器锁

GIL全局解释器锁:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念;随着电脑多核cpu的出现核cpu频率的提升,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,所以也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。

gil全局解释器锁的作用:多线程共享全局变量的时候,gil全局解释器锁可以保证同一时间内只有一个线程可以拿到这个全局变量;

看图讲解:

  1. t1拿到全局变量count=0
  2. t1申请gil锁(gil全局解释器锁可以保证只有一个线程可以拿到这个全局变量)
  3. t1调用系统线程
  4. t1到cpu上执行+1操作
  5. t1执行操作时可能偷懒了,没加完,但时间到了t1只能把gil锁给了t2
  6. t2在此之前已经拿到count全局变量count=0(注意此时以为t1没执行完+1操作,count=0)
  7. t2拿到gil锁
  8. t2调用系统线程
  9. t2到cpu上执行+1操作
  10. t2勤勤恳恳的+1,在时间内完成了加一操作
  11. t2 完成给count赋值(此时count=1)
  12. t1又拿到gil锁,继续未完成的操作,
  13. t1完成操作并赋值给count=1,(此时count还是=1)
#解决方法:调用threading模块的lock锁

import threading
global_num = 0
lock=threading.Lock() #实例化一个lock锁
def test1():
global global_num
lock.acquire() #锁上
for i in range(1000000):
global_num += 1
lock.release() #解锁 def test2():
global global_num
lock.acquire() #锁上
for i in range(1000000):
global_num += 1
lock.release() #解锁
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
print(global_num) .....................................运行结果
2000000

进程

  1. 一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的
  2. 进程之间是相互独立的
  3. 进程启动耗费的资源是比较大的,但是效率比较高
  4. cpu密集的时候适合用多进程
#多线程实现多任务

import multiprocessing,time
num = 10
def test1():
for i in range(num):
time.sleep(1)
print('task1',i)
def test2():
for i in range(num):
time.sleep(1)
print('task2',i)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
.............................运行结果 #共运行10s
#相互独立资源不共享

import multiprocessing
num = 0
def test1 ():
global num
for i in range(10):
num+=1
print(num)
def test2():
global num
for i in range(10):
num+=1
print(num)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start() ...............................运行结果 10
10
#进程池

import time ,multiprocessing
from multiprocessing import Pool def test1(n):
for i in range(n):
time.sleep
print('task1',i)
def test2(n):
for i in range(n):
time.sleep(1)
print('task2',i) if __name__ == '__main__':
pool = Pool(1) #允许最大进程数
pool.apply_async(test1,(10,)) #参数必须元组
pool.apply_async(test2,(10,))
pool.close()
pool.join()

协程

协程:也叫微线程,协程是在一个线程里面的

现有进程---> 线程 ---> 协程

  1. 进程是资源分配的单位
  2. 线程是操作系统调度的单位
  3. 协程的调度由所在程序自身控制
  4. 进程切换需要的资源最大,效率低
  5. 线程切换需要的资源一般,效率一般
  6. 协程切换任务资源很小,效率高
  7. 多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中

异步IO:遇到io请求就切换

第一步:pip 安装gevent模块

pip install gevent

协程实例:

#异步io

import gevent

def test1(n):
for i in range(n):
gevent.sleep(1)
print('task1',i)
def test2(n):
for i in range(n):
gevent.sleep(1)
print('task2',i)
g1 = gevent.spawn(test1,10) #协程任务g1
g2 = gevent.spawn(test2,10) #任务g2
g1.join()
g2.join() ..........................运行结果 #运行共用时10s

分析异步IO运行

协程完全靠异步IO完成两个任务:遇到io请求就切换(sleep算IO请求)

python ---线程,进程,协程的更多相关文章

  1. python 线程 进程 协程 学习

    转载自大神博客:http://www.cnblogs.com/aylin/p/5601969.html 仅供学习使用···· python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和 ...

  2. 学到了林海峰,武沛齐讲的Day34 完 线程 进程 协程 很重要

    线程 进程 协程 很重要 ...儿子满月回家办酒,学的有点慢,坚持

  3. Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】

    一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...

  4. 文成小盆友python-num11-(1) 线程 进程 协程

    本节主要内容 线程补充 进程 协程 一.线程补充 1.两种使用方法 这里主要涉及两种使用方法,一种为直接使用,一种为定义自己的类然后继承使用如下: 直接使用如下: import threading d ...

  5. python线程、协程、I/O多路复用

    目录: 并发多线程 协程 I/O多路复用(未完成,待续) 一.并发多线程 1.线程简述: 一条流水线的执行过程是一个线程,一条流水线必须属于一个车间,一个车间的运行过程就是一个进程(一个进程内至少一个 ...

  6. 15.python并发编程(线程--进程--协程)

    一.进程:1.定义:进程最小的资源单位,本质就是一个程序在一个数据集上的一次动态执行(运行)的过程2.组成:进程一般由程序,数据集,进程控制三部分组成:(1)程序:用来描述进程要完成哪些功能以及如何完 ...

  7. python之并发编程(线程\进程\协程)

    一.进程和线程 1.进程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是 ...

  8. python中线程 进程 协程

    多线程:#线程的并发是利用cpu上下文的切换(是并发,不是并行)#多线程执行的顺序是无序的#多线程共享全局变量#线程是继承在进程里的,没有进程就没有线程#GIL全局解释器锁#只要在进行耗时的IO操作的 ...

  9. python_21_线程+进程+协程

    python_线程_进程_协程 什么是线程? -- os能够进行运算调度的最小单位,被包含在进程之中,是一串指令的集合 -- 每个线程都是独立的,可以访问同一进程下所有的资源 什么是进程? -- 每个 ...

  10. 线程&进程&协程

    线程 线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务.Threading用 ...

随机推荐

  1. OneBlog开源博客-详细介绍如何实现freemarker自定义标签

    前言 OneBlog中使用到了springboot + freemarker的技术,同时项目里多个controller中都需要查询一个公有的数据集合,一般做法是直接在每个controller的方法中通 ...

  2. MVC和MVVM的差别

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码 ...

  3. 【JavaWeb】JavaScript 基础

    JavaScript 基础 事件 事件是指输入设备与页面之间进行交互的响应. 常用的事件: onload 加载完成事件:页面加载完成之后,常用于页面 js 代码初始化操作: onclick 单击事件: ...

  4. CopyOnWriteArrayList设计思路与源码分析

    CopyOnWriteArrayList实现了List接口,RandomAccess,Cloneable,Serializable接口. CopyOnWriteArrayList特性 1.线程安全,在 ...

  5. leetcode-242有效字母异位词

    题目 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词. 示例 1: 输入: s = "anagram", t = "nagaram&quo ...

  6. docker 报错: Cannot connect to the Docker daemon at unix:///var/run/docker.sock.

    最近在 Windows 子系统 WSL 上面安装了一个 ubuntu18.04, 安装完docker 跑 hello-world 的时候报错了 docker: Cannot connect to th ...

  7. 【Java】Jsoup爬虫,一个简单获取京东商品信息的小Demo

    简单记录 - Jsoup爬虫入门实战 数据问题?数据库获取,消息队列中获取中,都可以成为数据源,爬虫! 爬取数据:(获取请求返回的页面信息,筛选出我们想要的数据就可以了!) 我们经常需要分析HTML网 ...

  8. xtrabackup 备份与恢复

    书上摘抄 ---深入浅出mysql 448页  grant reload on *.* to 'backup'@'localhost' identified by '123456'; grant re ...

  9. vmstat参数详解

    vmstat 5 可以使用ctrl+c停止vmstat,可以看到输出依赖于所用的操作系统,因此可能需要阅读一下手册来解读报告 第一行的值是显示子系统启动以来的平均值,第二行开始展示现在正在发生的情况, ...

  10. 【ORA】ORA-16629解决办法

    数据库向保护模式报告不同的保护级别"警告消息. 首先查看主备库的保护模式和保护级别 select protection_mode,protection_level from v$databa ...