多线程和多进程的快速入门

多线程和多进程的快速入门

学习自:莫烦Python www.mofanpy.com

Threading - 多线程运算python程序

​ 多线程的简单理解:把数据分成很多段,将每一段数据放入一个线程,将所有的线程同时开始,大大的节省了运算时间。相当于:请一个人来工作一件事和请很多人来工作一件事

Threading 可以分配python分批量在同一时间做事情,但是Threading只能是在一个py脚本中

添加一个线程的案例

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()

程序运行的结果为:

This is a thread of <Thread(Thread-1, started 6300)>

threading中的join的功能

由于多线程是同石进行运行的线程任务,如果某些时刻你想等待所有的线程都运行完,然后开始某些操作,这个时候需要在这个操作前对每个线程使用join操作

join操作是等待threading线程运行完

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的结果如下:

T1 start

T2 start

T2 finish

T1 finish

all done

多线程搭配Queue功能

多线程无返回值,所以要把运算出来的结果放在一个长的队列中,对每一个线程的队列到主线程中拿出来,以便做继续的运算。

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()

该程序的结果如下:

[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]

多线程的效率分析

多线程的全局控制并不是把任务平均的分配个每一个线程 ,python只能让一个线程在某一时刻运算一个东西,然后不停的切换线程,让你误以为这些线程是在相同时刻在运算。 (学过操作系统应该会很容易理解多线程的并发运算)

下面实力代码中:

normal方法:对四倍的l列表执行累加运算

multithreading多线程方法:将四倍的l列表分为四份,用四个多线程同时处理

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)

程序的执行结果:

1999998000000
normal:  0.08538269996643066
1999998000000
multithreading:  0.08719158172607422

用了多线程比未用多线程的耗时反而多了

多线程与lock锁

锁的用法:当第一个线程处理完这一批数据得到一个初步的结果,然后再把这个结果拿去给第二个线程处理,这个时候就需要锁住第一个线程,等他处理完,再开始第二个线程

在要进行上锁的程序的前后设置锁

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进行操作

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再上锁 待程序运行完后 解锁

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中,等到所有的核运算完成后,再将结果从队列中取出,以便后续操作

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自己解决如何分配这些进程,如何分配运行出来的结果。

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()

程序运行的结果:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

多进程使用shared memory共享内存

为什么使用共享内存,不用全局变量?

因为多进程中传入全局变量给每个cpu,多进程对全局变量进行操作后传给其它进程是行不通的

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锁

锁的应用:

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这样两个多进程交替打印结果

多线程、多进程以及函数的效率对比

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)

运行的结果:

# 不用进程和线程
normal: 499999666667166666000000
normal time: 0.75795578956604
# 多线程
multithread: 499999666667166666000000
multithread time: 0.7419922351837158
# 多进程
multicore: 499999666667166666000000
multicore time: 0.4487142562866211

多进程运算的时间明显由于另外两个,多线程的运算与什么都不做时间差不多,说明多线程对于某些运算还是存在明显的短板

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值