python_多进程、多线程

多进程

1.创建进程

import multiprocessing as mp

def job(a):
    print('aaaa')

if __name__ == '__main__':
    p1 = mp.Process(target=job, args=(1,))
    p1.start()
    p1.join()

2.queue进程输出

import multiprocessing as mp

def job(q,a):
    res = 0
    for i in range(a):
        res += i
    q.put(res)
    
if __name__ == '__main__':
    q = mp.Queue()
    p1 = mp.Process(target=job, args=(q,3,))
    p2 = mp.Process(target=job, args=(q,2))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    res1 = q.get()
    res2 = q.get()
    p1.close()
    p2.close()
    print(res1 + res2)

3.效率对比(多线程与多进程)

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()
    p1.close()
    p2.close()
    res1 = q.get()
    res2 = q.get()
    print('multicore:',res1 + res2)

def multithread():
    q = mp.Queue() # thread可放入process同样的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)

def normal():
    res = 0
    for _ in range(2):
        for i in range(1000000):
            res += i + i**2 + i**3
    print('normal:', res)


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)

注意:其实这里跟数据规模和运算量有关系,如果将job中的1000000改成10,那么normal最快,multprocee最慢,因为创建进程也需要一定开销。如果把1000000改成10000000,那么multprocess就相对较快了,因为创建进程的开销相比于多进程节省的时间就可以忽略不记了。

4.进程池pool

import multiprocessing as mp

def job(x):
    return x*x

def main():
    pool = mp.Pool(processes=3) # 默认使用所有的核,可以用process指定核数
    res = pool.map(job,range(10))
    print(res)

    res = pool.apply_async(job,(2,))
    print(res.get())

    multi_res = [pool.apply_async(job,(i,)) for i in range(10)]
    print([res.get() for res in multi_res])
    

if __name__ == '__main__':
    main()

mp.Pool(process=)会将所有的数据推给指定的核;而使用apply_async可以将指定的数据推给一个核。如果想达到多核运算,可以用迭代的方式。

主要区别在于:直接使用mp.Pool指定process,那么一个进程可能会处理多次job,而如果用apply_async则是一个进程处理一个job。

5.共享内存

进程之间交流数据。(如果是多线程可以使用global)

import multiprocessing as mp

value = mp.Value('d', 0) # 'd'表示类型:double, 0表示数值
array = mp.Array('i', [1,2,3]) # 这里的array只是list,不能使用多维

6.进程锁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 main():
    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__':
    main()

使用进程锁可以使得进程之间不互相抢夺资源。一般如果我们想要一个进程处理完之后再去处理另一个进程时,就需要用到进程锁。

多线程

1.添加线程

import threading

def Myjob():
    print(f'this is an added Thread, number is {threading.current_thread()}')


def main():
    added_thread = threading.Thread(target=Myjob) # 添加一个线程,并指定该线程的工作内容
    added_thread.start() # 执行该线程
    # print(threading.active_count()) # 打印有几个激活的线程
    # print(threading.enumerate()) # 打印激活线程
    # print(threading.current_thread()) # 打印运行该程序的线程


if __name__ == '__main__':
    main()

2.join功能

join的意义:保证某个线程先执行完,再去执行主线程(如果有两个线程,它们都join了,表示当他们都执行完再去执行主线程,这两个线程不因执行join的先后而受影响

import threading
import time

def Myjob():
    print('Thread1 start\n')
    for i in range(10):
        time.sleep(0.1)
    print('Thread1 finish\n')

def main():
    added_thread = threading.Thread(target=Myjob, name='Thread1') # 添加一个线程,并指定该线程的工作内容
    added_thread.start() # 执行该线程
    print('all done\n')

if __name__ == '__main__':
    main()

结果:

Thread1 start

all done

Thread1 finish

可以发现all done在Thread1 finish之前打印了。因为这里使用了多线程,Thread1的执行不影响主程序线程的执行。

import threading
import time

def Myjob():
    print('Thread1 start\n')
    for i in range(10):
        time.sleep(0.1)
    print('Thread1 finish\n')

def main():
    added_thread = threading.Thread(target=Myjob, name='Thread1') # 添加一个线程,并指定该线程的工作内容
    added_thread.start() # 执行该线程
    added_thread.join() # Thread1执行完之后,再执行下面的语句
    print('all done\n')

if __name__ == '__main__':
    main()

结果:

Thread1 start

Thread1 finish

all done

可以发现加了added_thread.join()后,all done最后才打印,因此join函数的作用是保证该线程执行完成后,才执行下面语句。

import threading
import time

def Myjob():
    print('Thread1 start\n')
    for i in range(10):
        time.sleep(0.1)
    print('Thread1 finish\n')

def T2_job():
    print('T2 start\n')
    for i in range(5):
        time.sleep(0.1)
    print('T2 finish\n')

def main():
    added_thread = threading.Thread(target=Myjob, name='Thread1') # 添加一个线程,并指定该线程的工作内容
    thread2 = threading.Thread(target=T2_job, name='T2')
    thread2.start()
    added_thread.start() # 执行该线程
    thread2.join() # 这里保证thread2执行完成之后就可以执行后面语句了,不用管thread1是否执行完
    print('all done\n')

if __name__ == '__main__':
    main()

结果:

T2 start

Thread1 start

T2 finish

all done

Thread1 finish

发现all done在Thread1 finish前打印了,因为thread2.join()只保证thread2执行完成之后就可以执行后面语句了,不用管thread1是否执行完,因此只要执行完了thread2的工作(这里的标志是打印完T2 finish),就可以打印all done了。

3.Queue

Queue的意义:将所有进程的处理结果放到一个队列Queue中,再返回到主线程中做处理

import threading
import time
from queue import Queue

def job(l,q):
    # time.sleep(1)
    for i in range(len(l)):
        l[i] = l[i]**2
    q.put(l)


def main():
    q = Queue()
    threads = []
    data = [[1,2,3],[2,3,4],[3,4,5],[5,6]]
    for i in range(4):
        added_thread = threading.Thread(target=job, args=(data[i],q))
        added_thread.start()
        threads.append(added_thread)
    for thread in threads:
        thread.join()
    results = []
    for _ in range(4):
        results.append(q.get())
    print(results)

if __name__ == '__main__':
    main()

注意:q中获取的结果顺序不一定是[[1,4,9],[4,9,16],[9,16,25],[25,36]],如果将job()函数中的time.sleep(1)取消注释,就会发现结果并不是按data中的顺序计算的,他是按照哪个线程先完成的顺序排列的,即线程之间的结果顺序只有一个评判标准:哪个进程先完成(注意先开始的不一定先完成,只比较线程完成的时间点)

4.GIL不一定有效率

多线程看似是同时执行,其实是程序不停切换进程执行,让他看起来像是在同时进行。简单来说,多线程是并发,不是并行

多线程的意义:当cpu处于空闲时,可以先执行别的任务,从而提高效率。

就像前面代码中用到了time.sleep(1),如果只有一个线程,那么程序要先等休眠1秒后再继续执行任务。但如果是多线程的话, 程序可以在这休眠的1秒钟,先把其他线程的任务完成,这样就提高了效率。

5.锁(lock)

意义:如果线程1需要线程2的结果才能继续执行,那么可以锁住线程2,等线程2执行完再去执行线程1。

import threading
import time
from queue import Queue

def job1():
    global A, lock
    lock.acquire()
    for i in range(10):
        A = A + 1
        print('job1', A)
    lock.release()

def job2():
    global A, lock
    lock.acquire()
    A = 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()
    print(A)

保证在thread1执行完之后,thread2再执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值