https://blog.csdn.net/somezz/article/details/80963760

python 多线程

线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
---------------------

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。线程有 就绪、阻塞、运行 三种基本状态。

  1. 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;
  2. 运行状态是指线程占有处理机正在运行;
  3. 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。

---------------------

说到 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() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。

使用线程池来执行线程任务的步骤如下:

  1. 调用 ThreadPoolExecutor 类的构造器创建一个线程池。
  2. 定义一个普通函数作为线程任务。
  3. 调用 ThreadPoolExecutor 对象的 submit() 方法来提交线程任务。
  4. 当不想提交任何任务时,调用 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多线程,多进程,线程池,进程池相关推荐

  1. python线程池模块_Python并发编程之线程池/进程池--concurrent.futures模块

    一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...

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

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

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

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

  4. python 进程池 freeze_support_Python 多进程并发操作中进程池Pool的实例

    在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.当被操作对象数目不大时,可以直接利用multiprocessing中的Process ...

  5. python进程池调用实例方法_Python 多进程并发操作中进程池Pool的实例

    在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.当被操作对象数目不大时,可以直接利用multiprocessing中的Process ...

  6. Python爬虫:通信和线程池进程池

    通信和线程池进程池 线程间通信 导入线程队列 from queue import Queue import time from random import randint 同一个进程中的多个线程可以直 ...

  7. 并发编程 - 线程 - 1.线程queue/2.线程池进程池/3.异步调用与回调机制

    1.线程queue :会有锁 q=queue.Queue(3) q.get() q.put() 先进先出 队列后进先出 堆栈优先级队列 1 """先进先出 队列" ...

  8. 爬虫day8通信和线程池进程池总结

    day8通信和线程池进程池总结 一.线程间通信 1.线程间通信 同一个进程中的多个线程可以直接通信(一个线程可以直接使用另外一个线程中产生的数据) 通信原则:使用全局变量 from threading ...

  9. 进程、线程、进程池、进程三态、同步、异步、并发、并行、串行

    点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 来源:cnblogs.com/songhaixing/p/1 ...

  10. 定时器 线程池\进程池

    定时器 可以指定线程多久后启动,(并且他是一个异步的线程,也就是并发) from threading import Timer """ 这个相当于开启了一个可以定义时间的 ...

最新文章

  1. Hadoop 4、Hadoop MapReduce的工作原理
  2. 使用Go语言访问JSON数据(gojsonq)
  3. python修改excel数据-python之实现对excel表格数据的修改
  4. 通过/proc/PID/status查看进程内存占用情况
  5. Arduino UNO WIFI开发板,学习必备开发套件
  6. 基础练习 查找整数 c语言
  7. 华为云大数据存储的冗余方式是三副本_华为OceanStor分布式存储,引领智能时代大数据创新...
  8. 影响局域网速度的因素
  9. clover更新驱动 后不能开机_黑苹果笔记本电池模式下省电驱动,延长笔记本续航...
  10. linux系统怎么安装python3视频教程_Linux系统,python3.7安装教程
  11. PAT (Basic Level) Practice1023 组个最小数
  12. Java爬虫Crawler
  13. 用php实现遍历目录
  14. ERP操作手册要不要做?
  15. window下的批处理命令学习
  16. idea好用插件及常用开发小工具、文本编辑器(替代notepad++)合集
  17. ER studio使用
  18. 修改窗口的风格ModifyStyle
  19. 22年字节跳动飞书人力套件二面面经
  20. 远期债券收益率期望曲率调整

热门文章

  1. 21 世纪最需要的 7 种人才素质 - 李开复
  2. 转:复杂网络分析总结
  3. 【Java多线程系列七】ExecutorService
  4. luogu P1586 四方定理(背包)
  5. [算法]两个栈实现一个队列
  6. ELK 6.2版本部署
  7. 7.QT-Qt对象间的父子关系
  8. matlab predict函数并行,Matalb 智能算法第29个案例运行报错问题
  9. php_self include,PHP_SELF返回/index.php/index.php
  10. android sdk system images,ADT中使用Android SDK Manager安装X86的System Image | 在路上