多任务
并发:在一段时间内交替执行多个任务
并行:在一段时间内同事一起执行多个任务
进程 Process
进程:一个程序运行在系统之上, 便称这个程序喂一个运行进程,并分配进程ID方便系统管理。操作系统进行资源分配和调度运行的基本单位
单进程 vs 多进程
+/ 如果程序向利用计算机的多核优势,让CPU同时处理一些任务,适合用多进程开发
先挖个坑后面学
线程 thread
线程是属于进程的,一个进程可以开启多个线程,执行不同工作,是进程实际工作最小单位
同属一个进程的多个线程共享进程所拥有的全部资源
性质
执行顺序
无序,通过CPU调度决定某个线程先执行
结束顺序
主线程会等待所有子线程执行结束后再结束
基本使用
创建线程对象
thread_obj = thread.Thread(target=func)
启动线程执行
thread_obj.start()
import threading
import time
def thread1():
while True:
print('thread 1')
time.sleep(1)
def thread2():
while True:
print('thread 2')
time.sleep(1)
if __name__ == '__main__':
# create thread
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
# start thread
t1.start()
t2.start()
等待当前线程任务执行完毕后再向下继续执行
thread_obj.join()
执行带有参数的任务
args:以元组方式进行传参 *参数顺序保持一致
kwargs:以字典的方式给任务传参 *字典key和参数名保持一致
守护线程
主线程不等待子线程执行完成,主线程执行完毕后,子线程也自动关闭
me,thod1
threading.Thread(target = work, daemon = True)
method2
线程对象.setDaemon(True)
获取线程信息
current_thread = threading.current_thread()
print(current_thread)
设置/获取名称
name = 线程对象.current_thread().getName()
线程对象.setName()
自定义线程
class Mythread(threading.Thread):
def run(self):
print('执行此线程', self.args)
t = Mythread(args = (100, ))
t.start()
线程安全
多个线程操作可能会出现数据混乱的情况
*有些数据类型(如列表)是线程安全的
GIL锁
全局解释器锁(Global Interpreter Lock),让一个进程中同一个时刻只能有一个线程可以被CPU调用
Lock 同步锁
创建锁
lock_object = threading.RLock()
加锁
线程对象.acquire()
释放锁
线程对象.release()
e.g
import threading
lock_object = threading.Lock()
loop = 1000
number = 0
def _add(count):
lock_object.acquire() #加锁
global number
for i in range(count):
number += 1
print("t1")
lock_object.release() #解锁
def _sub(count):
lock_object.acquire() #申请锁
global number
for i in range(count):
number -= 1
print("t2")
lock_object.release() #解锁
t1 = threading.Thread(target=_add,args=(loop,))
t2 = threading.Thread(target=_sub,args=(loop,))
t1.start()
t2.start()
t1.join()
t2.join()
print(number)
RLock 递归锁
Lock不支持锁的嵌套,RLock支持,就是可以加多次锁
Dead Lock 死锁
由于资源竞争或者彼此通信造成阻塞
线程池
线程不是开的越多越好,开的多了可能导致系统性能降低(线程中的上下文切换),因此不建议无限制的创建线程,建议使用线程池
创建
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(100) #100个线程的线程池
提交任务
pool.submit(task, sug)
等待线程池执行完毕
pool.shutdown(True)
空闲线程处理额外任务
线程池中提交一个任务,如果有空闲线程,则分配一个线程去执行,执行完毕后再将线程交还给线程池,如果没有空闲线程,则等待
future =pool.submit(task,url)
future.add_done_callback(done)
单例模式
import threading
import time
class Singleton:
instance = None
lock = threading.RLock()
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs): #*args,**kwargs 代表任意多个参数
# 这两行提高效率,可要可不要
if cls.instance:
return cls.instance
with cls.lock:
if cls.instance:
return cls.instance
cls.instance = object.__new__(cls)
return cls.instance
def task():
obj = Singleton('x')
print(obj)
for i in range(10):
t = threading.Thread(target=task)
t.start()
线程和进程对比
关系
线程依附在进程里面,没有进程就没有线程
一个进程有一个及以上线程
区别
创建进程的资源开销比创建线程的资源开销大
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
线程不能独立执行,必须依存在进程中
优缺点
进程:可多核,开销大
线程:不可多核,开销小
应用:
计算密集型,多进程。 e.g大量的数据计算
IO密集型,多线程 e.g 文件读写,网络数据传输