Python多线程多进程

Python多线程多进程

线程是计算机CPU调度的对最小单元(真正在工作),进程是计算机资源分配的最小单元(进程为线程提供资源)。
一个进程可以有很多线程,同一个进程中的线程可以共享此进程中的资源。
由于GIL锁的存在,控制一个进程中同一时刻只有一个线程可以被CPU调度。

GIL锁

为了解决多线程共享内存的数据安全问题,引入了GIL锁,全称为Global Interpreter Lock,也就是全局解释器锁。

GIL规定,在一个进程中每次只能有一个线程在运行。这个GIL锁相当于是线程运行的资格证,某个线程想要运行,首先要获得GIL锁,然后遇到IO或者超时的时候释放GIL锁,给其余的线程去竞争,竞争成功的线程获得GIL锁得到下一次运行的机会。

正是因为有GIL的存在,python的多线程其实是假的,所以才有人说python的多线程非常鸡肋。但是虽然每个进程有一个GIL锁,进程和进程之间还是不受影响的。

CPU密集型操作使用多进程比较合适,例如海量运算。
IO密集型操作使用多线程比较合适,例如爬虫,文件处理,批量ssh操作服务器等等。

一、多线程

1、没有使用线程

import time
def work(i):
    print('{}开始任务'.format(i))
    time.sleep(3)
    print('{}结束任务'.format(i))


start_time = time.time()
for i in range(5):
    work(i)
print('所有的任务完成')

end_time = time.time()
print(end_time - start_time)

# 0开始任务
# 0结束任务
# 1开始任务
# 1结束任务
# 2开始任务
# 2结束任务
# 3开始任务
# 3结束任务
# 4开始任务
# 4结束任务
# 所有的任务完成
# 15.042697191238403

2、使用多线程

import threading
import time
def work(i):
    print(threading.current_thread())
    print('{}开始任务'.format(i))
    time.sleep(3)
    print('{}结束任务'.format(i))

start_time = time.time()
ts = []
for i in range(5):
    t = threading.Thread(target=work,args=(i,))
    t.start()
    ts.append(t)

for t in ts:
    t.join()

print('所有的任务完成')
end_time = time.time()
print(end_time - start_time)

# <Thread(Thread-4, started 17792)>
# 0开始任务<Thread(Thread-5, started 26072)>
# 1开始任务
# <Thread(Thread-6, started 16180)>
# 2开始任务
# <Thread(Thread-7, started 2492)>
# 3开始任务
# <Thread(Thread-8, started 15588)>
# 4开始任务
# 3结束任务4结束任务
# 1结束任务
# 0结束任务2结束任务
# 所有的任务完成
# 3.0036520957946777

没有使用线程的时间是15秒,使用线程的时间是3秒,多线程的时间是执行最久的线程的时间。

3、线程池

from concurrent.futures import ThreadPoolExecutor
import threading
def work(i):
    print(threading.current_thread())
    print('{}开始任务'.format(i))
    time.sleep(3)
    print('{}结束任务'.format(i))
    return i

with ThreadPoolExecutor() as pool:
    result = pool.map(work,list(range(5)))

    for res in result:
        print(res)

# <Thread(ThreadPoolExecutor-0_0, started 21760)>
# 0开始任务
# <Thread(ThreadPoolExecutor-0_1, started 16872)>
# 1开始任务
# <Thread(ThreadPoolExecutor-0_2, started 18192)>
# 2开始任务
# <Thread(ThreadPoolExecutor-0_3, started 17596)>
# 3开始任务
# <Thread(ThreadPoolExecutor-0_4, started 1360)>
# 4开始任务
# 2结束任务0结束任务4结束任务
# 3结束任务1结束任务
# 0
# 1
# 2
# 3
# 4

4、线程的通信安全

import threading

count = 0

def fun_add():
    global count
    for i in range(1000000):
        count += 1

def fun_sub():
    global count
    for i in range(1000000):
        count -= 1

t1 = threading.Thread(target=fun_add)
t2 = threading.Thread(target=fun_sub)
t1.start()
t2.start()
t1.join()
t2.join()
print(count)

这段程序执行结果count不等于0,存在通信安全,所以要使用锁

5、线程锁

锁 lock= threading.RLock()

lock.acquire() 加锁
lock.release()释放锁
两个线程用同一把锁

列表append extend 索引取值 pop sort 都是线程安全的 不用加锁

Lock同步锁 不支持连续锁 但锁一次解一次的效率比递归锁高
RLock 递归锁 支持锁多次
推荐使用 RLock

import threading

count = 0
lock = threading.RLock()
def fun_add():
    global count
    for i in range(1000000):
        lock.acquire()
        count += 1
        lock.release()

def fun_sub():
    global count
    for i in range(1000000):
        lock.acquire()
        count -= 1
        lock.release()

t1 = threading.Thread(target=fun_add)
t2 = threading.Thread(target=fun_sub)
t1.start()
t2.start()
t1.join()
t2.join()
print(count)

# 0

二、多进程

1、没有使用多进程

import time
def cal(s,e):
    sum = 0
    for i in range(s,e):
        sum = sum + i

    return sum

start_time = time.time()
a = cal(1,100000000)
end_time = time.time()
print(end_time - start_time)
# 3.229938268661499

2、使用多进程

多进程放在 if name == 'main’中 执行

from multiprocessing import Process,Queue
import time

def cal(q,s,e):
    sum = 0
    for i in range(s,e):
        sum = sum + i
    print(sum)
    q.put(sum)

if __name__ == '__main__':
    start_time = time.time()
    q = Queue()

    p = Process(target=cal, args=(q, 0,  50000000))
    p1 = Process(target=cal, args=(q, 50000000, 100000000))
    p.start()
    p1.start()
    p.join()
    p1.join()
    print('主进程获取Queue数据')
    print(q.get()+q.get())
    print('结束测试')
    end_time = time.time()
    print(end_time - start_time)

# 3749999975000000
# 1249999975000000
# 主进程获取Queue数据
# 4999999950000000
# 结束测试
# 1.9793572425842285

CPU密集型的程序使用多进程要比不使用时间快,上面代码使用队列Queue进行数据通信,put将数据存放在队列中,get获取队列中的数据

3、使用进程池,用队列进行数据通信

使用进程池,队列的方法用Manager().Queue()

# 使用进程池
from multiprocessing import Manager,Pool
import time

def cal(q,s,e):
    sum = 0
    for i in range(s,e):
        sum = sum + i
    print(sum)
    q.put(sum)

if __name__ == '__main__':
    start_time = time.time()
    q = Manager().Queue()
    pool = Pool(2)

    for i in range(2):
        pool.apply_async(func=cal,args=(q,i*50000000,(1+i)*50000000))
    pool.close()
    pool.join()

    print('主进程获取Queue数据')
    print(q.get()+q.get())
    print('结束测试')
    end_time = time.time()
    print(end_time - start_time)

# 1249999975000000
# 3749999975000000
# 主进程获取Queue数据
# 4999999950000000
# 结束测试
# 2.4994184970855713

4、使用Manager进行数据共享

使用Manager可以对共享的数据进行修改

from multiprocessing import Manager,Pool
import time

def cal(datas,s,e):
    print(s)
    sum = 0
    for i in range(s,e):
        sum = sum + i
    datas.append(sum)

if __name__ == '__main__':
    pool = Pool(2)
    with Manager() as manager:
        start_time = time.time()

        datas = manager.list()

        process_list = []
        for i in range(2):
            pool.apply_async(func=cal,args=(datas,i*50000000,(1+i)*50000000))

        pool.close()
        pool.join()

        print('主进程获取datas数据')
        print(datas[0]+datas[1])
        print('结束测试')
        end_time = time.time()
        print(end_time - start_time)
        print(datas)

# 1249999975000000
# 3749999975000000
# 主进程获取datas数据
# 4999999950000000
# 结束测试
# 1.6817915439605713
# [1249999975000000, 3749999975000000]
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值