Python 并发1: 进程,线程

相信大家在操作系统就了解过 进程,线程,死锁,系统调度等等,对基本的概念还是有的。但是,在实践的过程上我在python上的实现却略有不足。所以重新入门Python并发。

线程 Thread

所谓线程,线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。进程由操作系统创建,在操作系统执行进程的时候,一般会给进程分配空间等资源。而线程既可以由进程创建,也可以由操作系统创建,通过使用父进程所分配到的资源进行命令的执行调度,这里不详细展开,会在Linux入门中更新。

  • Python 中创建多线程的方法: threading. 通过 threading.Thread 创建事例对象,当start启动后即可执行线程。同时,线程的执行可能收到分时,处理器的调度策略等乱序执行。
>>> from threading import *
>>> import time
>>> def print_Thread(name = 'None'):
...     print('I am ' + name)
...     time.sleep(5)
...     print('Yes, I am ' + name)
>>> for i in range(3):
...     t = Thread(target = print_Thread, args = (str(i),))    # 这里的target是方法的调用
...     t.start()     # 生成进程,t是主线程,生成线程执行 print_Thread的进程为子线程。start之后生成子线程后主线程的逻辑结构就结束了,但是主线程会等待子线程结束后再结束.主线程如果出现问题,子线程也会死亡。
...     print('zhu_end')
I am 0
zhu_end
I am 1
zhu_end
I am 2
zhu_end
>>>
>>> Yes, I am 0
Yes, I am 1
Yes, I am 2
  • Python同时提供一些方便我们进行线程管理的API:

    threading.enumerate() 查看当前运行的线程数

>>> def print_t():
...     import time
...     time.sleep(5)
...     print('This is a threading')
>>> if __name__ == '__main__':
...     from threading import *
...     for i in range(5):
...         t = Thread(target = print_t)
...         t.start()           # 子线程在这里被创建
...     print(enumerate())
...
[<_MainThread(MainThread, started 10864)>, <Thread(Thread-6, started 21948)>, <Thread(Thread-7, started 14448)>, <Thread(Thread-8, started 5916)>, <Thread(Thread-9, started 15372)>, <Thread(Thread-10, started 15664)>]
>>> This is a threading
This is a threading
This is a threading
This is a threading
This is a threading
  • 线程代码的封装: 通过继承Thread类完成类线程的创建。这里来做一些接口的拓展:start() 每个thread 对象都只能被调用1次start() run() 如果创建Thread的子类,重写该方法。负责执行target参数传来的可执行对象。
from threading import *
import time
class Th_son(Thread):def __init__(self,myname:str):Thread.__init__(self)                # 因为__init__会覆写父进程,也就是Thread的初始化。所以要在吃实话之中重新进行弗雷德初始化来达到调用的方法self.myname = myname def change(self,myname:str):             used_name = self.mynameself.myname = mynameprint('Name from ' + used_name + ' change to ' + self.myname)time.sleep(1)def print_name(self):print('My name is ' + self.myname)time.sleep(1)def run(self):self.print_name()
#         myname = input()                    # 在python中子进程里如果调用input会出错。self.change('New Thread2')  if __name__ == '__main__':New_Th = Th_son(myname = 'New Thread 1')print(enumerate())New_Th.start()                           # 因为在Python中复写了RUN, 不需要再进行复写(多态),调用Thread的方法Start()print(enumerate())[<_MainThread(MainThread, started 5172)>]
[<_MainThread(MainThread, started 5172)>, <Th_son(Thread-12, started 3876)>]
  • 关于多线程之间的传参: 全局变量的共享

    首先明确全局变量的变化: 如果创建的可变类型变量,全局变量不需要声明global,但是可变类型由于在函数中你能找到他的地址,所以可以不用加global。 多线程间可以共享全局变量,包括使用args传参的方法。(因为太简单就不放代码了)

  • 线程的互斥锁。众所周知,资源的进程,线程的切换都是不可抗力使得结果与我们有较大偏差,因此我们引申锁得概念。这个操作系统都讲过,不再细说。 避免死锁的办法可以通过添加超时时间等方法解决

# python锁接口(互斥锁):
# 注意,因为mutex一般是对全局的阻塞, 所以mutex需要设置为全局变量OK?
mutex = threading.Lock()    # 锁创建
mutex.acquire()             # 进行锁定
mutex.release()             # 锁释放

Python慢的深度原因: GIL

Python 慢的原因: A. 动态类型语言, 边解释边执行

​ B. GIL 无法利用多核CPU并发执行

什么是GIL: 全局解释器锁(Global Interpreter Lock , GIL) 是计算机程序设计语言解释器(CPython解释器),用于同步线程的一种机制。它使得任何时刻仅有一个线程再运行。即使在多核处理器上,使用GIL解释器也只允许同一时间执行一个线程 。依赖于IO的释放。所以IO密集型对多线程可以最大发挥优势,所以在爬虫中经常使用分布式爬虫。

解决GIL带来的限制,Python multiprocessing多进程机制实现真正的并行计算,利用多核CPU优势。

进程 Process

操作系统调度的基本单位 。是包括,资源,PCB,代码上下文,可执行的代码等一系列的集合。Tips: 进程与线程相比有自己分配的资源,同时也意味着进程切换开销要大很多。同时进行子进程的创建时,需要拷贝主进程的资源拷贝,时空间消耗很大。进程之间有相互通讯的模块(队列),但整体来说数据的协同没有同一进程内的线程方便。在python中,多进程可以理解为浪费了空间等资源,但达到了并发,减少了时间

  • 进程的创建:multiprocessing
    (注,在windows系统上不允许子进程使用print 打印信息,所以在这里可以用命令行,或者改用Linux系统跑代码才会有print输出)
from multiprocessing import Process
import os
from time import sleep# 子进程要执行的代码
def run_proc(name, age, **kwargs):for i in range(10):#在Win下的python环境中,子进程是无法进行Print打印的,所以我们采取的方法最好就是用Linux的虚拟机啦,从这个代码之后所有的代码是虚拟机版本print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age,os.getpid()))print(kwargs)sleep(0.5)if __name__=='__main__':print('父进程 %d.' % os.getpid())p = Process(target=run_proc, args=('test',18), kwargs={"m":20})print('子进程将要执行')p.start()sleep(1)p.terminate()p.join()print('子进程已结束')C:\Users\40629>python C:\Users\40629\Desktop\Untitled-1.py
父进程 19680.
子进程将要执行
子进程运行中,name= test,age=18 ,pid=18484...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=18484...
{'m': 20}
子进程已结束
  • 子进程继承父进程的资源,同时继承父进程的环境变量
>>> from multiprocessing import *
>>> import os
>>> def show():
...     try:
...         print(os.environ['NewProcessComming'])
...     except:
...         print('Not success')
...
>>> if __name__ == '__main__':
...     os.environ['NewProcessComming'] = '/bin/bash'
...     p1 = Process(target = show)
...     p1.start()
...
>>> /bin/bash
# 重新进入PYTHON, 新开一个进程
>>> import os
>>> os.environ['NewProcessComming']
Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/usr/lib/python3.8/os.py", line 675, in __getitem__raise KeyError(key) from None
KeyError: 'NewProcessComming'
# 环境变量资源没有保存哦,说明上面子进程确实是继承了父进程的资源,也会导致伊西俄安全问题
  • 进程间的通信,通过操作系统来实现(SOCKET),一般是采用队列的数据结构存储数据与进行数据的交换: Queue
# 基础API:
q= mulyiprocessing.Queue(space)            # 队列对象的实例化.Tips:q在多线程通讯之间需要以传参的方式传递才能发挥作用
q.put()                                     # 存数据,在python中数据类型任意。如果Q队列满了,就会阻塞等待
q.get()                                     # 取数据,如果取数据的时候队列为空,就会阻塞等待
q.full()                                    # 判断是否满
q.empty()                                   # 是否空

实例Show: (扩展,在不同主机间网络通讯实现消息队列,Redis, Kafka)

>>> from multiprocessing import *
>>> import time
>>> def read(queue):
...     while(True):
...         if queue.empty():
...            time.sleep(1)
...         m = queue.get()
...         print('Done Reading ' + str(m))
>>> def write(queue):
...     write_num = 0
...     while(True):
...         if queue.full():
...            time.sleep(1)
...         queue.put(write_num)
...         print('Done Writing ' + str(write_num))
...         write_num += 1
>>> if __name__ == '__main__':
...     queue = Queue(3)
...     p1 = Process(target = read, args = (queue,))
...     p2 = Process(target = write, args = (queue,))
...     p1.start()
...     p2.start()Done Writing 0
Done Writing 1
Done Writing 2
Done Reading 0
Done Reading 1
.......
  • 进程池:当创建的进程目标成千上万,创建进程工作量巨大,同时创建,回收的代价太高,因此考虑multiprocessing.Pool,也就是进程池

初识化进程池时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没满就会创建一个新的进程执行改请求。但是如果进程池被取空了,请求就会被阻塞知道有进程空出来。

from multiprocessing import Pool
import os
from time import sleep
def test():print("I'm Process " + str(os.getpid()))if __name__ == '__main__':process_pool = Pool(3)for i in range(5):process_pool.apply(test)# 先关闭进程池在等待所有进程结束process_pool.close()# 阻塞主进程process_pool.join()print('___end____')I'm Process 15824
I'm Process 15824
I'm Process 8876
I'm Process 12408
I'm Process 15824
___end____

可以发现进程号相同,换句话说进程池减少了进程创建和销毁的过程.

Python 并发1 进程,线程相关推荐

  1. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块

    本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...

  2. Python并发编程之线程池/进程池

    引言 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我 ...

  3. python并发编程-进程池线程池-协程-I/O模型-04

    目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...

  4. python并发编程--进程、线程、协程、锁、池、队列

    文章目录 操作系统的概念 进程 multiprocessing模块 守护进程 使用多进程实现一个并发的socket的server 锁 生产者消费者模型 数据共享 线程threading模块 守护线程和 ...

  5. Python异常处理和进程线程

    写在前面 最坏的结果,不过是大器晚成: 一.异常处理 - 1.语法错误导致的异常 - 这种错误,根本过不了python解释器的语法检测,必须在程序运行前就修正: - 2.逻辑上的异常 - 即逻辑错误, ...

  6. Python并发编程之线程的玩法

    一.线程基础以及守护进程 线程是CPU调度的最小单位 全局解释器锁 全局解释器锁GIL(global interpreter lock) 全局解释器锁的出现主要是为了完成垃圾回收机制的回收机制,对不同 ...

  7. python多线程执行其他模块的文件_python并发编程--进程线程--其他模块-从菜鸟到老鸟(三)...

    concurrent模块 1.concurrent模块的介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 Proc ...

  8. Python 并发编程--进程,线程,协程

    并发编程 基本概念的区分: 并发 只有一个CPU,多个程序在一个CPU上轮流执行,宏观上多个进程并发执行,但微观上依旧是串行 并行 有多个CPU,多个程序在多个CPU上同时执行. 进程 计算机中最小的 ...

  9. HIT软件构造 第十章 并发编程 进程 线程 线程安全

    名词解释 进程(Process)和线程(thread):并发编程的两个基本单元.进程:(1)和同一个机器上的其他进程是彼此隔离的.(2)拥有私有的内存空间,运行时不能共享变量.(3)通过IPC(pip ...

最新文章

  1. 三层交换机的热备实验
  2. 关于Public key for *.rpm is not installed 的解决方法
  3. ECharts Tooltip
  4. 添加别名_ssh别名免密登陆服务器
  5. ajax div 赋值重新渲染_优化向:单页应用多路由预渲染指南
  6. 特殊符号的写法 (HTML 4.01 符号实体)
  7. ssm框架下开发RESTful json简单实例
  8. matlab幅度归一化,matlab归一化方法
  9. 书单:《人人都是产品经理》附录书单
  10. 老哥们 FlexiTimer库怎么用不了呢 ,指点一下小弟
  11. 循环时尚是消费者与电商平台的一场“双向奔赴”?
  12. 我国三大运营商即将开始联手屏蔽垃圾短信
  13. 蜜蜂遗传学在社会传播中塑造肠道微生物群的菌株结构
  14. 全力冲unreal了
  15. java微信支付代码_10行代码搞定微信支付(Java版)
  16. [游戏数据分析]WAU模型简介及WAU预测
  17. Java学习笔记 算法 Algorithms Fourth Edition
  18. element plus 表el-table的多个行列合并
  19. 用计算机汇编语言的程序是经过,汇编语言程序
  20. 作为一名开发者,这个七夕你(打算)怎么过?

热门文章

  1. WebGL基础教程之 三维透视投影
  2. adaptec raid linux,Adaptec - Adaptec RAID 6805E
  3. 将csdn的博客爬取到本地并输出为jekyll可解析的markdown格式,同时保存博客的图片到本地
  4. 摘自网络的html基本标签
  5. blender php,Blender 学习笔记
  6. bing.com 重定向次数过多
  7. 苹果电脑python编程里面怎么切到中文_Mac苹果笔记本上实现Python多版本相互切换功能...
  8. 物联网中传感器和执行器的类型
  9. 苏宁要让iPhone用户年年半价换新 直击刚需?
  10. MySQL索引的优点和缺点