1、概念
cpu执行程序的最小单位,从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。Python可以运行多线程,但和单核CPU多进程一样,在给定时刻只有一个线程会执行。
2、一个进程资源包含多个线程
from threading import Thread from multiprocessing import Process import os,time,random def func(num): time.sleep(random.uniform(0.1,1)) print("当前进程{},参数是{}".format(os.getpid(),num)) for i in range(10): t = Thread(target=func,args=(i,)) t.start() print(os.getpid())
3、并发的多线程比并发多进程速度快
def func(num): print("当前进程{},参数是{}".format(os.getpid(),num)) if __name__ == "__main__": # 多线程 lst = [] # 记录开始时间 startime = time.time() for i in range(1000): t = Thread(target=func,args=(i,)) t.start() lst.append(t) # 等到所有的子线程执行完毕 for i in lst: i.join() # 计算结束时间 endtime = time.time() print("多线程执行时间",(endtime - startime)) # 0.18 # 多进程 lst = [] # 记录开始时间 startime = time.time() for i in range(1000): t = Process(target=func,args=(i,)) t.start() lst.append(t) # 等到所有的子线程执行完毕 for i in lst: i.join() # 计算结束时间 endtime = time.time() print("多进程执行时间",(endtime - startime)) # 44.41
4、多线程之间共享同一份进程资源
num = 1000 def func(): global num num -= 1 for i in range(1000): t = Thread(target=func) t.start() print(num)
5、自定义线程
from threading import Thread import os import time class MyThread(Thread): def __init__(self,name): # 手动调用父类的构造方法 super().__init__() self.name = name def run(self): time.sleep(1) print("当前进程号码是{},名字是{}".format(os.getpid() , self.name)) if __name__ == "__main__": t = MyThread("当前是一个线程") t.start() print("主线程执行结束 ... ")
6、线程相关函数
线程.is_alive() 检测线程是否仍然存在 线程.setName() 设置线程名字 线程.getName() 获取线程名字 1.currentThread().ident 查看线程id号 2.enumerate() 返回目前正在运行的线程列表 3.activeCount() 返回目前正在运行的线程数量
7、守护线程,等待所有线程全部执行完毕之后,自己在终止,守护所有线程
from threading import Thread import time def func1(): while True: time.sleep(0.5) print("我是func1") def func2(): print("我是func2 start ... ") time.sleep(3) print("我是func2 end ... ") def func3(): print("我是func3 start ... ") time.sleep(5) print("我是func3 end ... ") if __name__ == "__main__": t1 = Thread(target=func1) t2 = Thread(target=func2) t3 = Thread(target=func3) # 在start调用之前,设置线程为守护线程 t1.setDaemon(True) t1.start() t2.start() t3.start() print("主线程执行结束 .... ")
8、线程安全问题
from threading import Lock,Thread import time n = 0 def func1(lock): global n lock.acquire() for i in range(1000000): n += 1 lock.release() def func2(lock): global n # with 自动完成上锁+解锁 with lock: for i in range(1000000): n -= 1 if __name__ == "__main__": lst = [] lock = Lock() startime = time.time() for i in range(10): t1 = Thread(target= func1,args=(lock,)) t2 = Thread(target= func2,args=(lock,)) t1.start() t2.start() lst.append(t1) lst.append(t2) for i in lst: i.join() endtime = time.time() # 88.30 1.84 print("主线程执行结束 ... 打印{} 时间是{}".format(n,endtime-startime))
9、信号量 Semaphore (线程)
from threading import Semaphore , Thread import time def func(i,sm): # 上锁 + 解锁 with sm: print(i) time.sleep(3) if __name__ == "__main__": # 支持同一时间,5个线程上锁 sm = Semaphore(5) for i in range(20): Thread(target=func,args=(i,sm)).start() """ 再创建线程的时候是异步创建 在执行任务时,遇到Semaphore进行上锁,会变成同步程序 """
10、死锁 互斥锁 递归锁
死锁分为语法和逻辑死锁,只上锁不解锁
递归锁专门用来解决这种死锁现象或临时用于快速解决线上项目发生阻塞死锁问题的
互斥锁(尽量用一把锁解决问题)
11、事件
from threading import Event,Thread import time,random """ e = Event() # wait 动态添加阻塞 # clear 将内部的阻塞值改成False # set 将内部的阻塞值改成True # is_set 获取内部的阻塞值状态(True False) """ # 基本语法 e = Event() print(e.is_set()) e.set() print(e.is_set()) e.clear() print(e.is_set()) # 代表最多阻塞3秒 e.wait(3) print("程序运行中... ")
12、线程缺陷
python线程可以并发,但是不能并行,同一个进程下的多个线程不能分开被多个cpu同时执行。
原始是因为python自带gil全局锁,限制同一个时间,一个进城下多个线程只能被一个cpu执行,不能实现并行操作,python是解释性语言,执行一句编译一句,而不是一次性全部编译成功,不能提前规划,都是临时调度,容易造成cpu调度异常,所以加一把GIL全局锁,想要线程并行解决,使用多进程间接实现线程并行或使用jpython解释器
13、线程队列(相同数据类型)
from queue import Queue """ put 存 get 取 put_nowait 存,超出了队列长度,报错 get_nowait 取,没数据的时,直接报错 linux windows 线程中 put_nowait get_nowait 都支持 """ # (1) Queue """先进先出,后进后出""" q = Queue() q.put(1) q.put(2) print(q.get()) print(q.get()) # 取出不来,阻塞 # print(q.get()) # 没有数据时,报错 # print(q.get_nowait()) # 指定队列长度 q2 = Queue(3) q2.put(100) q2.put(101) # q2.put(102) # 存放的数据超出了队列长度,阻塞 # q2.put(103) q2.put_nowait(104) # (2)LifoQueue 先进后出,后进先出(栈的特点) from queue import LifoQueue lq = LifoQueue(3) lq.put(11) lq.put(22) lq.put(33) # lq.put_nowait(44) error print(lq.get()) print(lq.get()) print(lq.get()) # print(lq.get()) 阻塞 # (3)PriorityQueue 按照优先级顺序进行排序(默认从小到大) from queue import PriorityQueue pq = PriorityQueue() # 可以存放数字 # pq.put(80) # pq.put(81) # pq.put(18)
14、线程池
# ThreadPoolExecutor 线程池的基本使用 from threading import current_thread as cthread def func(i): print("thread ... start" ,cthread().ident ) print("thread ... end " , i) return cthread().ident if __name__ == "__main__": lst = [] setvar = set() # (1) 创建线程池对象 """参数: 默认并发的线程数 是 os.cpu_count() * 5 = 40""" tp = ThreadPoolExecutor() # (2) 异步提交任务 """默认如果一个线程短时间内可以完成更多的任务,线程池就不会使用更多的线程来完成,以节省资源""" for i in range(100): res = tp.submit(func,10) lst.append(res) # (3) 获取返回值 for i in lst: setvar.add(i.result()) # (4) 等待所有子线程执行结束之后,在执行主线程 # tp.shutdown() print("主线程执行结束 .... ") print(setvar , len(setvar)) """ 无论是进程池还是线程池,都是由固定的进程数或者线程数来执行所有的任务.并不对额外创建多余的进程或者线程. """ # (3) 线程池 map from collections import Iterator,Iterable def func(i): # 同一时间5个线程执行任务 print("thread ... " ,cthread().ident ) return i * "*" if __name__ == "__main__": # 创建线程池对象(最大允许并发5个线程) tp = ThreadPoolExecutor(5) # 把执行的结果返回到迭代器中 it = tp.map(func,range(20)) # 判断返回值是迭代器 print(isinstance(it,Iterator)) # 等待所有子线程执行结束. tp.shutdown() # 遍历迭代器 for i in it: print(i)
15、进程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import os,time def func(i): print("任务执行中 ... start" , os.getpid()) time.sleep(3) print("任务执行结束 ... end " , i) return i # ProcessPoolExecutor 进程池的基本使用 if __name__ == "__main__": lst = [] # cpu逻辑核心数 # print(os.cpu_count()) # (1) 创建进程池对象 """参数: 默认获取的是最大cpu逻辑核心数 8""" p = ProcessPoolExecutor(8) # (2) 异步提交任务 """默认如果一个进程短时间内可以完成更多的任务,进程池就不会使用更多的进程来完成,以节省资源""" for i in range(10): res = p.submit(func,i) lst.append(res) # (3) 获取当前进程任务中的返回值(result在获取任务的返回值时,有阻塞) # for i in lst: # print(i.result()) # (4) 等待所有子进程执行结束之后,在继续执行主进程内容(shutdown) p.shutdown() # <=> join print("<=======>") print(os.getpid())
标签:__,python,put,线程,time,print,import From: https://www.cnblogs.com/songyunjie/p/16829593.html