线程池和进程池

线程池和进程池

线程安全

​ 单线程程序不会有线程安全的问题,但多线程程序就可能出现线程安全的问题,举个例子,当多个线程同时访问同一个数据时并对其做出操作时,可能会出错,原因是线程一访问某个数据并修改了它,但还没来得及保存,线程二也访问了这个数据,但取到的数据还是未修改的,所以线程二取到的是一个脏数据(错误数据)。

线程安全解决方案——加锁

1.Lock锁

from threading import Thread,Lock#导入线程模块和Lock锁模块
account = 10000#定义一个全局变量,表示账户余额
account_lock = Lock()#创建锁对象,一个数据对应一个锁对象
#定义一个存钱的函数
def save_money(num):
    print(f'开始存钱')
    global account
    account_lock.acquire()#加锁
    account += num
    print(f'存钱成功,当前余额:{account}')
    account_lock.release()#解锁
#定义一个取钱函数
def draw_money(num):
    print(f'开始存钱')
    global account
    account_lock.acquire()#加锁
    if account < num:
        return
    account -= num
    print(f'存钱成功,当前余额:{account}')
    account_lock.release()#解锁
#创建子线程对象
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
#启动子线程
t1.start()
t2.start()

运行结果

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/02-Lock锁.py
开始取钱
开始存钱
取钱成功,当前余额:5000
存钱成功,当前余额:7000

Process finished with exit code 0

2.RLock锁

from threading import Thread,RLock
import time
account = 10000
account_lock = RLock()
def save_money(num):
    #RLock锁只需要加锁,with作用域的代码执行完后会自动解锁
    with account_lock:
        print('开始存钱')
        global account
        balance = account
        time.sleep(2)
        account = num + balance
        print(f'存钱成功,当前余额:{account}')
def draw_money(num):
    with account_lock:
        print('开始取钱')
        global account
        balance = account
        if balance >= num:
            account = balance - num
            time.sleep(2)
            print(f'取钱成功,当前余额:{account}')
        else:
            print('余额不足')
t1 = Thread(target=save_money,args=(2000,))
t2 = Thread(target=draw_money,args=(5000,))
t2.start()
t1.start()

运行结果同上

队列

​ 队列是一个容器,而且和迭代器比较像,只能一个一个往外去数据,和迭代器不同是队列除了有将数据放进去的进口,还有将数据取出的出口,相当于一个管道,也就是说队列的数据是先进先出,而迭代器里的数据是先进后出,队列里的数据无法遍历,队列还有一个特点,就是在用死循环取队列的数据时不会报错,因为在取完队列数据后队列会进入等待状态,等到有数据放进队列就继续取,如下:

from queue import Queue
q = Queue#创建队列对象
for i in range(10):
    q.put(i)#放数据
while True:
    num = q.get()#取数据
    print(num)
 

运行结果:代码不会报错,但也不会结束,队列在死循环中会一直等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JopjABdi-1604494280484)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201104200844753.png)]

线程池

线程池可以理解为将多个子线程统一管理工具

实例代码

from threadpool import ThreadPool,makeRequests
import time
#定义一个函数
def func(a):
    time.sleep(1)
    print(a)
#创建线程池对象
t_pool = ThreadPool(10)#线程池有10个子线程
#创建任务列表/makeRequests(任务对应的函数,函数的参数列表)
task_lst = makeRequests(func,[x for x in range(10)])
#在线程池中添加任务
for task in task_lst:
    t_pool.putRequest(task)
start_time = time.time()
t_pool.wait()#执行和等待
end_time = time.time()
print(f'执行时间:{end_time-start_time}s')

运行结果:其执行时间是单线程执行时间的1/10

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
6
4
3
2
1
0
5
8
9
7
执行时间:1.0014607906341553s

Process finished with exit code 0

进程池

​ 进程池的概念和线程池一样,进程池就是多个子进程的管理工具,不同之处在于线程池里的每个线程可以访问并操作同一个数据,而进程池不行,进程池在创建时,会在每个子进程里拷贝一份主进程的数据,子进程只能操作拷贝的数据,无法操作主进程的数据

from multiprocessing import Pool
import time
def func(a):
    time.sleep(1)
    print(a)
if __name__=='__main__':
    #创建进程池对象
    p_pool = Pool(3)#3个子进程
    start_time = time.time()
    #添加任务
    p_pool.map(func,[x for x in range(9)])#添加多个任务
    #p_pool.apply(func,(10,))#添加一个任务
    #p_pool.apply_async(func,(11,))#添加一个任务,不过是和主进程异步执行,前两个都是和主进程同步执行
    """线程池添加任务后会自动启动"""
    p_pool.close()#关闭线程池
    p_pool.join()#等待线程池的任务执行完成
    end_time = time.time()
    print(f'执行时间:{end_time-start_time}s')

运行结果:执行时间是单进程执行时间的1/3

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s

Process finished with exit code 0

执行时间是单进程执行时间的1/3

H:\Python虚拟环境\Spyder\Scripts\python.exe H:/Day8ThreadAndProcess/test.py
0
1
2
3
4
5
6
7
8
执行时间:3.5766236782073975s

Process finished with exit code 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值