day8-线程安全-锁-队列-多进程和多线程
一、线程安全的起因:多个线程可以同时操作同一个数据
解决方法:枷锁
LOCK 锁 - RLOCK锁
首先要导入锁
from threading import Lock,RLock
1.保证一个数据对应一个锁对象
锁对象.Lock()
2.线程在第一次操作数据前加锁
锁对象.acquire()
3.线程在结束数据操作后解锁
锁对象.release()
RLOCK锁
1.保证一个数据对应一个锁对象
锁对象.RLock()
2.将对数据进行操作的代码放大要加锁的范围
with 锁对象:
操作数据的代码块
二、队列的使用
from queue import Queue
1.什么是队列
a.Queue是一种容器,一般用于多线程多进程的数据共享,因为你Queue对象本身有线程安全
b.获取队列中的元素的时候,如果没有元素会等待,直到队列中有元素为止
2.操作队列
a.添加元素: 队列对象.put(元素)
b.获取元素:队列对象.get()
queue = Queue()
queue.put(元素) - 添加元素
queue.get() - 获取元素
线程与进程区别
-
进程是资源分配的基本单位,线程是CPU执行和调度的基本单位
-
通信/同步方式:
-
- 进程:
- 通信方式:管道,FIFO,消息队列,信号,共享内存,socket,stream流;
- 同步方式:PV信号量,管程
- 线程:
- 同步方式:互斥锁,递归锁,条件变量,信号量
- 通信方式:位于同一进程的线程共享进程资源,因此线程间没有类似于进程间用于数据传递的通信方式,线程间的通信主要是用于线程同步。
- 进程:
-
多线程
import time
from datetime import datetime
from threading import Thread
def download(name):
print(f'{name}开始下载.{datetime.now()}')
time.sleep(2)
print(f'{name}下载结束.{datetime.now()}')
进程默认有一个线程,这个线程叫主线程;其他的线程都叫子线程
python中threading模块中Thread类的对象就是线程对象 (程序中需要多少个线程就创建多少个Thread类或者他的子类的对象)
-
创建线程对象
-
Thread(target=函数,args=参数)
-
函数 - 需要在子线程中调用的函数(普通函数,匿名函数)
-
参数 - 传一个 元组,默认是();元组中的元素就是调用函数的时候需要传递的实参
t1 = Thread(target=download,args=('复仇',)) t2 = Thread(target=download,args=('信条',))
-
-
启动线程 - 在子线程中执行任务 线程对象.start()
t1.start() t2.start()
-
我们可以通过
join
方法让主线程阻塞,等待其创建的线程执行完成。import threading import time def tstart(arg): print("%s running....at: %s" % (arg,time.time())) time.sleep(1) print("%s is finished! at: %s" % (arg,time.time())) if __name__ == '__main__': t1 = threading.Thread(target=tstart, args=('This is thread 1',)) t1.start() t1.join() # 当前线程阻塞,等待t1线程执行完成 print("This is main function at:%s" % time.time())
执行结果为:
This is thread 1 running....at: 1564906617.43 This is thread 1 is finished! at: 1564906618.43 This is main function at:1564906618.43
线程池
-
创建任务列表
makeRequests(任务对应的函数,参数列表)
1)任务函数有且只有一个参数2)参数列表中元素的个数决定了任务列表中任务的个数
tasks = makeRequests(download,[f'电影{x}' for x in range(100)]
-
创建线程池对象
ThreadPool(线程的数量)
pool = ThreadPool(10)
-
在线程池中添加任务
线程池对象.putRequest(任务对象)
for task in tasks: pool.putRequest(task)
进程池
# 1.创建进程池
# Pool(进程数量)
pool = Pool(3)
# 2.添加任务(任务添加完成后会自动启动)
# 1)一次性添加多个任务(和主进程同步执行)
# 进程池对象.map(函数,参数列表)
pool.map(download,[f'电影{x}' for x in range(5)])
# 一次添加一个任务
pool.apply(download,('肖生克的救赎',))
# 3.关闭进程池
# 进程池关闭后不能再往进程池中添加任务
pool.close()
# 4.等待
pool.join()