多线程和多进程的快速入门
学习自:莫烦Python www.mofanpy.com
Threading - 多线程运算python程序
多线程的简单理解:把数据分成很多段,将每一段数据放入一个线程,将所有的线程同时开始,大大的节省了运算时间。相当于:请一个人来工作一件事和请很多人来工作一件事
Threading 可以分配python分批量在同一时间做事情,但是Threading只能是在一个py脚本中
添加一个线程的案例
python
import threading
#def main():
# print(threading.active_count()) # 查看现在激活线程的数量
# print(threading.enumerate()) # 查看当前的threading都是什么
# print(threading.current_thread()) # 查看我运行这个函数程序的时候 对应的threading是什么
def thread_job():
print('This is a thread of %s' % threading.current_thread()) # 查看我运行这个函数程序的时候 对应的threading是什么
def main():
thread = threading.Thread(target=thread_job,) # 添加线程 这个线程做什么由target指定
thread.start() # 添加一个线程后,要启动线程,才能让这个线程开始工作
if __name__ == '__main__':
main()
程序运行的结果为:
python
This is a thread of <Thread(Thread-1, started 6300)>
threading中的join的功能
由于多线程是同石进行运行的线程任务,如果某些时刻你想等待所有的线程都运行完,然后开始某些操作,这个时候需要在这个操作前对每个线程使用join操作
join操作是等待threading线程运行完
python
import threading
import time
def thread_job():
print('T1 start\n')
for i in range(10): time.sleep(0.1) # 睡一秒
print('T1 finish\n')
def T2_job():
print('T2 start\n')
print('T2 finish\n')
def main():
added_thread = threading.Thread(target=thread_job, name='T1') # name为给线程命名
thread2 = threading.Thread(target=T2_job, name='T2')
added_thread.start()
thread2.start()
thread2.join()
added_thread.join()
print('all done\n')
if __name__ == '__main__':
main()
不加join的结果如下:
T1 start
T2 start
all done
T2 finish
T1 finish
加join的结果如下:
python
T1 start
T2 start
T2 finish
T1 finish
all done
多线程搭配Queue功能
多线程无返回值,所以要把运算出来的结果放在一个长的队列中,对每一个线程的队列到主线程中拿出来,以便做继续的运算。
python
import threading
import time
from queue import Queue
def job(l,q):
# 对每一个列表中的值做平方运算
for i in range(len(l)):
l[i] = l[i]**2
q.put(l)
def multithreading():
q = Queue() # 定义队列
threads = [] # 用来存储所有的线程
data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]
for i in range(4): # 定义四个线程 计算大列表中的四个小列表
t = threading.Thread(target=job, args=(data[i], q)) # args用来给线程 传递参数
t.start() # 启动线程
threads.append(t) # 将每个小列表的计算线程 存储到线程列表中
for thread in threads: thread.join() # 等到所有的线程运行完 再继续操作
results = []
for _ in range(4): results.append(q.get()) # 将每个线程运算的结果 保存
print(results)
if __name__ == '__main__':
multithreading()
该程序的结果如下:
python
[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
多线程的效率分析
多线程的全局控制并不是把任务平均的分配个每一个线程 ,python只能让一个线程在某一时刻运算一个东西,然后不停的切换线程,让你误以为这些线程是在相同时刻在运算。 (学过操作系统应该会很容易理解多线程的并发运算)
下面实力代码中:
normal方法:对四倍的l列表执行累加运算
multithreading多线程方法:将四倍的l列表分为四份,用四个多线程同时处理
python
import threading
from queue import Queue
import copy
import time
def job(l, q):
res = sum(l)
q.put(res)
def multithreading(l):
q = Queue()
threads = []
for i in range(4):
t = threading.Thread(target=job, args=(copy.copy(l), q), name='T%i' % i)
t.start()
threads.append(t)
[t.join() for t in threads]
total = 0
for _ in range(4):
total += q.get()
print(total)
def normal(l):
total = sum(l)
print(total)
if __name__ == '__main__':
l = list(range(1000000))
s_t = time.time()
normal(l*4)
print('normal: ',time.time()-s_t)
s_t = time.time()
multithreading(l)
print('multithreading: ', time.time()-s_t)
程序的执行结果:
python
1999998000000
normal: 0.08538269996643066
1999998000000
multithreading: 0.08719158172607422
用了多线程比未用多线程的耗时反而多了
多线程与lock锁
锁的用法:当第一个线程处理完这一批数据得到一个初步的结果,然后再把这个结果拿去给第二个线程处理,这个时候就需要锁住第一个线程,等他处理完,再开始第二个线程
在要进行上锁的程序的前后设置锁
python
import threading
def job1():
global A, lock
lock.acquire() # 在要进行上锁的程序前 设置锁
for i in range(10): # 对A进行10次累加1
A += 1
print('job1', A)
lock.release() # 在要进行上锁的程序后 设置锁
def job2():
global A, lock
lock.acquire()
for i in range(10): # 对A进行10次累加10
A += 10
print('job2', A)
lock.release()
if __name__ == '__main__':
lock = threading.Lock() # 定义锁
A = 0 # 定义一个全局变量 模拟共享内存
t1 = threading.Thread(target=job1)
t2 = threading.Thread(target=job2)
t1.start()
t2.start()
t1.join()
t2.join()
不加锁的程序结果: 两个线程同时运行,结果上为交替对A进行操作
python
job1 1
job1 2
job1 13
job2 12
job1 14
job2 24
job1 25
job2 35
job1 36
job2 46
job1 47
job2 57
job1 58
job2 68
job1 69
job2 79
job1 80
job2 90
job2 100
job2 110
加锁的程序结果: job1先上锁 待程序运行完后 解锁。此时job2再上锁 待程序运行完后 解锁
python
job1 1
job1 2
job1 3
job1 4
job1 5
job1 6
job1 7
job1 8
job1 9
job1 10
job2 20
job2 30
job2 40
job2 50
job2 60
job2 70
job2 80
job2 90
job2 100
job2 110
Multiprocessing - 多进程运算python程序
运用多核CPU处理器CPU运算python程序,一般只会用一个核来处理程序,但是现代计算器的处理器一般都是多核,如果不用白白的浪费,可以将其运用起来加速程序的处理。
多线程实际上还是同一个时间处理一个线程(并发轮转调度),而多核完全避免了这种情况,它会把任务平均分配给每一个核,每一个核都有单独的运算空间和运算能力,多核从一定程度上避免了多线程劣势。
创建进程
import multiprocessing as mp
def job(q):
print("进程创建", q)
if __name__ == '__main__':
p1 = mp.Process(target=job, args=(q,))
p1.start()
进程与Queue输出
将每一个核运算出来的结果放入到队列Queue中,等到所有的核运算完成后,再将结果从队列中取出,以便后续操作
python
import multiprocessing as mp
def job(q):
res = 0
for i in range(1000):
res += i+i**2+i**3
q.put(res) # 将运算后的值操作放入到队列
if __name__ == '__main__':
q = mp.Queue() # 定义队列
p1 = mp.Process(target=job, args=(q,)) # 注意args 即使只有一个参数 也要写成(q,) 不然会报错
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get() # 从队列中获取值
res2 = q.get()
print(res1+res2)
运行结果为:499667166000
进程池
进程池就是把所有要运行的东西放到一个池子里,python自己解决如何分配这些进程,如何分配运行出来的结果。
python
import multiprocessing as mp
def job(x): # 平方操作
return x*x
def multicore():
pool = mp.Pool(processes=2) # 默认的是你所有的核心, processes是指定想用的核的数量
res = pool.map(job, range(10)) # map 将range(10) 交给job,并将结果返回
print(res)
res = pool.apply_async(job, (2,)) # 只传入一个值给job 如果输入的是(2,3,4,5)会报错,因为只能传入一个值
print(res.get()) # 使用get获取
multi_res =[pool.apply_async(job, (i,)) for i in range(10)] # 利用迭代器 获取对多个值操作的结果
print([res.get() for res in multi_res]) # 利用列表迭代获取
if __name__ == '__main__':
multicore()
程序运行的结果:
python
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
多进程使用shared memory共享内存
为什么使用共享内存,不用全局变量?
因为多进程中传入全局变量给每个cpu,多进程对全局变量进行操作后传给其它进程是行不通的
python
import multiprocessing as mp
value = mp.Value('i', 1) # i是整数 d是double小数 f是float小数等等
array = mp.Array('i', [1, 2, 3, 4]) # 只能是一维列表,不能是多维
value和array可以被每一个核读取,加载到内存
多进程与lock锁
锁的应用:
python
import multiprocessing as mp
import time
def job(v, num, l):
l.acquire()
for _ in range(10):
time.sleep(0.1)
v.value += num
print(v.value)
l.release()
def multicore():
l = mp.Lock() # 实例化锁
v = mp.Value('i', 0) # 设立共享内存变量
p1 = mp.Process(target=job, args=(v, 1, l))
p2 = mp.Process(target=job, args=(v, 3, l))
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
multicore()
加锁的结果为:
前面程序依次+1 后面程序依次+3
不加锁的结果为:
3 4 7 8 11 12这样两个多进程交替打印结果
多线程、多进程以及函数的效率对比
python
import multiprocessing as mp
import threading as td
import time
def job(q):
res = 0
for i in range(1000000):
res += i+i**2+i**3
q.put(res) # queue
def multicore():
q = mp.Queue()
p1 = mp.Process(target=job, args=(q,))
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get()
res2 = q.get()
print('multicore:' , res1+res2)
def normal():
res = 0
for _ in range(2):
for i in range(1000000):
res += i+i**2+i**3
print('normal:', res)
def multithread():
q = mp.Queue()
t1 = td.Thread(target=job, args=(q,))
t2 = td.Thread(target=job, args=(q,))
t1.start()
t2.start()
t1.join()
t2.join()
res1 = q.get()
res2 = q.get()
print('multithread:', res1+res2)
if __name__ == '__main__':
st = time.time()
normal()
st1= time.time()
print('normal time:', st1 - st)
multithread()
st2 = time.time()
print('multithread time:', st2 - st1)
multicore()
print('multicore time:', time.time()-st2)
运行的结果:
python
# 不用进程和线程
normal: 499999666667166666000000
normal time: 0.75795578956604
# 多线程
multithread: 499999666667166666000000
multithread time: 0.7419922351837158
# 多进程
multicore: 499999666667166666000000
multicore time: 0.4487142562866211
多进程运算的时间明显由于另外两个,多线程的运算与什么都不做时间差不多,说明多线程对于某些运算还是存在明显的短板