python多线程,多进程,线程池,进程池
https://blog.csdn.net/somezz/article/details/80963760
python 多线程
线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
---------------------
每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。线程有 就绪、阻塞、运行 三种基本状态。
- 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
- 运行状态是指线程占有处理机正在运行;
- 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。
---------------------
说到 Python 中的多线程,一个绕不过去的话题就是全局锁 GIL(Global interpreter lock)。GIL 本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
首先明确我们线程执行的任务是什么,是做计算(计算密集型)还是做输入输出(I/O 密集型),不同地场景使用不同的方法。Python 多线程适合用在 I/O 密集型任务中。I/O 密集型任务较少时间用在 CPU 计算上,较多时间用在 I/O 上,如文件读写,web 请求,数据库请求 等;而对于计算密集型任务,应该使用多进程。
利用threading模块使用多线程
Python标准库自带了两个多线程模块,分别是threading和thread,其中,thread是低级模块,threading是对thread的分装,一般,我们直接使用threading即可。
threading 模块提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
下面的代码设置了5个线程调用say_hello函数
import threading
import timedef say_hello():time.sleep(1)print("Hello world!")def main():threads = []for i in range(5):thread = threading.Thread(target=say_hello)thread.start()threads.append(thread)for thread in threads:thread.join()print('hello')main()
输出
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
hello
jion()函数阻塞主线程的运行。没有使用join()函数,线程执行顺序不定,主线程可能在所有子线程执行完之前就执行了。
import threading
import timedef say_hello():time.sleep(1)print("Hello world!")def main():for i in range(5):thread = threading.Thread(target=say_hello)thread.start() print('hello')main()
输出
hello
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
python多线程和多进程之间的比较
Python 多线程适合用在 I/O 密集型任务中。I/O 密集型任务较少时间用在 CPU 计算上,较多时间用在 I/O 上,如文件读写,web 请求,数据库请求 等;而对于计算密集型任务,应该使用多进程。
https://blog.csdn.net/somezz/article/details/80963760
线程同步之 Lock (互斥锁):
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,这个时候就需需要使用互斥锁来进步同步。如下所示的代码,在三个线程对共同变量 num 进行 100 万次加减操作之后,其 num 的结果不为 0,
import time, threadingnum = 0
lock = threading.Lock()
def task_thread(n):global numfor i in range(1000000):num = num + nnum = num - nt1 = threading.Thread(target=task_thread, args=(6,))
t2 = threading.Thread(target=task_thread, args=(17,))
t3 = threading.Thread(target=task_thread, args=(11,))
t1.start(); t2.start(); t3.start()
t1.join(); t2.join(); t3.join()
print("except value is 0, real value is {}".format(num))
运行结果
except value is 0, real value is 23
之所以会出现不为 0 的情况,因为修改 num 需要多条语句,当一个线程正在执行 num+n 时,另一个线程正在执行 num-m ,从而导致之前的线程执行 num-n 时 num 的值已不是之前的值,从而导致最终的结果不为 0 。
为了保证数据的正确性,需要使用互斥锁对多个线程进行同步,限制当一个线程正在访问数据时,其他只能等待,直到前一线程释放锁。使用 threading.Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。如下:
import time, threadingnum = 0
lock = threading.Lock()
def task_thread(n):global num# 获取锁,用于线程同步lock.acquire()for i in range(1000000):num = num + nnum = num - n#释放锁,开启下一个线程lock.release()t1 = threading.Thread(target=task_thread, args=(6,))
t2 = threading.Thread(target=task_thread, args=(17,))
t3 = threading.Thread(target=task_thread, args=(11,))
t1.start(); t2.start(); t3.start()
t1.join(); t2.join(); t3.join()
print("except value is 0, real value is {}".format(num))
except value is 0, real value is 0
也可以使用with lock:
import time, threadingnum = 0
lock = threading.Lock()
def task_thread(n):global numwith lock:for i in range(1000000):num = num + nnum = num - nt1 = threading.Thread(target=task_thread, args=(6,))
t2 = threading.Thread(target=task_thread, args=(17,))
t3 = threading.Thread(target=task_thread, args=(11,))
t1.start(); t2.start(); t3.start()
t1.join(); t2.join(); t3.join()
print("except value is 0, real value is {}".format(num))
线程池
概述
https://www.jianshu.com/p/afd9b3deb027
传统多线程方案会使用“即时创建, 即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。
一个线程的运行时间可以分为3部分:线程的启动时间、线程体的运行时间和线程的销毁时间。在多线程处理的情景中,如果线程不能被重用,就意味着每次创建都需要经过启动、销毁和运行3个过程。这必然会增加系统相应的时间,降低了效率。
使用线程池:
由于线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
线程池的使用
http://c.biancheng.net/view/2627.html
线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。
如果使用线程池/进程池来管理并发编程,那么只要将相应的 task 函数提交给线程池/进程池,剩下的事情就由线程池/进程池来搞定。
Exectuor 提供了如下常用方法:
- submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
- map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
- shutdown(wait=True):关闭线程池。
程序将 task 函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。
Future 提供了如下方法:
- cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
- cancelled():返回 Future 代表的线程任务是否被成功取消。
- running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
- done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
- result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
- exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
- add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。
使用线程池来执行线程任务的步骤如下:
- 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
- 定义一个普通函数作为线程任务。
- 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
- 当不想提交任何任务时,调用 ThreadPoolExecutor 对象的 shutdown() 方法来关闭线
from concurrent.futures import ThreadPoolExecutor
import time# 定义一个准备作为线程任务的函数
def action(max):time.sleep(2)return max
# 创建一个包含2条线程的线程池
pool = ThreadPoolExecutor(max_workers=2)
# 向线程池提交一个task, 50会作为action()函数的参数
future1 = pool.submit(action, 50)
# 向线程池再提交一个task, 100会作为action()函数的参数
future2 = pool.submit(action, 100)
# 关闭线程池
pool.shutdown()
另外,由于线程池实现了上下文管理协议(Context Manage Protocol),因此,程序可以使用 with 语句来管理线程池,这样即可避免手动关闭线程池,如上面的程序所示。
from concurrent.futures import ThreadPoolExecutor
import time# 定义一个准备作为线程任务的函数
def action(max):time.sleep(2)return max
# 创建一个包含2条线程的线程池
with ThreadPoolExecutor(max_workers=2) as pool:# 向线程池提交一个task, 50会作为action()函数的参数future1 = pool.submit(action, 50)# 向线程池再提交一个task, 100会作为action()函数的参数future2 = pool.submit(action, 100)# 查看future1代表的任务返回的结果print(future1.result())# 查看future2代表的任务返回的结果print(future2.result())
此外,Exectuor 还提供了一个 map(func, *iterables, timeout=None, chunksize=1)
方法,该方法的功能类似于全局函数 map(),区别在于线程池的 map() 方法会为 iterables 的每个元素启动一个线程,以并发方式来执行 func 函数。这种方式相当于启动 len(iterables) 个线程,井收集每个线程的执行结果。
例如,如下程序使用 Executor 的 map() 方法来启动线程,并收集线程任务的返回值:
from concurrent.futures import ThreadPoolExecutor
import time# 定义一个准备作为线程任务的函数
def action(max):time.sleep(2)return max
# 创建一个包含2条线程的线程池
with ThreadPoolExecutor(max_workers=2) as pool:# 向线程池提交4个task, (50,100,150,200)会作为action()函数的参数results = pool.map(action, (50,100,150,200))for result in results:# 查看future代表的任务返回的结果print(result)
进程池
https://www.jianshu.com/p/53c2e732d974
concurrent.futures
库提供了一个 ProcessPoolExecutor
类, 可被用来在一个单独的Python解释器中执行计算密集型函数。
from concurrent.futures import ProcessPoolExecutor
import timedef work(x):time.sleep(2)return result# Parallel implementation
with ProcessPoolExecutor() as pool:results = pool.map(action, (50,100,150,200))for result in results:# 查看future代表的任务返回的结果print(result)
像多线程那样可以使用pool.submit()手动提交单个任务。
python多线程,多进程应用
python 多线程下载图片
https://www.52pojie.cn/thread-912305-1-1.html?tdsourcetag=s_pctim_aiomsg
Python多进程处理图片
https://blog.csdn.net/Alvin_FZW/article/details/82886004
python多线程,多进程,线程池,进程池相关推荐
- python线程池模块_Python并发编程之线程池/进程池--concurrent.futures模块
一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...
- Python并发编程之线程池/进程池
引言 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我 ...
- python 进程池 freeze_support_Python 多进程并发操作中进程池Pool的实例
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.当被操作对象数目不大时,可以直接利用multiprocessing中的Process ...
- python进程池调用实例方法_Python 多进程并发操作中进程池Pool的实例
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.当被操作对象数目不大时,可以直接利用multiprocessing中的Process ...
- Python爬虫:通信和线程池进程池
通信和线程池进程池 线程间通信 导入线程队列 from queue import Queue import time from random import randint 同一个进程中的多个线程可以直 ...
- 并发编程 - 线程 - 1.线程queue/2.线程池进程池/3.异步调用与回调机制
1.线程queue :会有锁 q=queue.Queue(3) q.get() q.put() 先进先出 队列后进先出 堆栈优先级队列 1 """先进先出 队列" ...
- 爬虫day8通信和线程池进程池总结
day8通信和线程池进程池总结 一.线程间通信 1.线程间通信 同一个进程中的多个线程可以直接通信(一个线程可以直接使用另外一个线程中产生的数据) 通信原则:使用全局变量 from threading ...
- 进程、线程、进程池、进程三态、同步、异步、并发、并行、串行
点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 来源:cnblogs.com/songhaixing/p/1 ...
- 定时器 线程池\进程池
定时器 可以指定线程多久后启动,(并且他是一个异步的线程,也就是并发) from threading import Timer """ 这个相当于开启了一个可以定义时间的 ...
最新文章
- Hadoop 4、Hadoop MapReduce的工作原理
- 使用Go语言访问JSON数据(gojsonq)
- python修改excel数据-python之实现对excel表格数据的修改
- 通过/proc/PID/status查看进程内存占用情况
- Arduino UNO WIFI开发板,学习必备开发套件
- 基础练习 查找整数 c语言
- 华为云大数据存储的冗余方式是三副本_华为OceanStor分布式存储,引领智能时代大数据创新...
- 影响局域网速度的因素
- clover更新驱动 后不能开机_黑苹果笔记本电池模式下省电驱动,延长笔记本续航...
- linux系统怎么安装python3视频教程_Linux系统,python3.7安装教程
- PAT (Basic Level) Practice1023 组个最小数
- Java爬虫Crawler
- 用php实现遍历目录
- ERP操作手册要不要做?
- window下的批处理命令学习
- idea好用插件及常用开发小工具、文本编辑器(替代notepad++)合集
- ER studio使用
- 修改窗口的风格ModifyStyle
- 22年字节跳动飞书人力套件二面面经
- 远期债券收益率期望曲率调整
热门文章
- 21 世纪最需要的 7 种人才素质 - 李开复
- 转:复杂网络分析总结
- 【Java多线程系列七】ExecutorService
- luogu P1586 四方定理(背包)
- [算法]两个栈实现一个队列
- ELK 6.2版本部署
- 7.QT-Qt对象间的父子关系
- matlab predict函数并行,Matalb 智能算法第29个案例运行报错问题
- php_self include,PHP_SELF返回/index.php/index.php
- android sdk system images,ADT中使用Android SDK Manager安装X86的System Image | 在路上