multiprocessing是python封装的包含了调用和进程间通讯的多进程类库, 无论密集型运算还是IO堵塞都能用, 副作用小, 但最重。

threading是基于系统级的线程调用, 适用的场合通常是IO堵塞。多线程的优势是切换快资源消耗低,但一个线程挂掉则会影响到所有线程,所以不够稳定。现实中使用线程池的场景会比较多gevent和asycio是基于协程的python网络库,在遇到IO阻塞时,程序会自动进行切换,可以让我们用同步的方式写异步IO代码。

总结: IO 密集型一般使用多线程或者多进程,CPU 密集型一般使用多进程,强调非阻塞异步并发的一般都是使用协程

多线程

因为有GIL的存在,所以Python中的多线程不能真正的利用多核,对于计算密集型的任务多线程是鸡肋的。但是对于IO密集型,例如爬虫,文件读写等多线程是完全可行的,因为网络IO的延迟比CPU的更大。

1.threading实例

如果要实现主线程和子线程的同步,我们必需使用join方法

import threading
import time

def long_time_task(i):
    print('当前子线程: {} 任务{}'.format(threading.current_thread().name, i))
    time.sleep(2)
    print("结果: {}".format(8 ** 20))

if __name__=='__main__':
    start = time.time()
    print('这是主线程:{}'.format(threading.current_thread().name))
    thread_list = []
    for i in range(1, 3):
        t = threading.Thread(target=long_time_task, args=(i, ))
        thread_list.append(t)
        t.start()
    for t in thread_list:
        t.join()

    end = time.time()
    print("总共用时{}秒".format((end - start)))

>>> 这是主线程:MainThread
    当前子线程: Thread-1 任务1
    当前子线程: Thread-2 任务2
    结果: 1152921504606846976
    结果: 1152921504606846976
    总共用时2.001833915710449

2.继承Thread类重写run方法创建新线程

import threading
import time

def long_time_task(i):
    time.sleep(2)
    return 8**20

class MyThread(threading.Thread):
    def __init__(self, func, args , name='', ):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args
        self.name = name
        self.result = None

    def run(self):
        print('开始子进程{}'.format(self.name))
        self.result = self.func(self.args[0],)
        print("结果: {}".format(self.result))
        print('结束子进程{}'.format(self.name))

if __name__=='__main__':
    start = time.time()
    threads = []
    for i in range(1, 3):
        t = MyThread(long_time_task, (i,), str(i))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

    end = time.time()
    print("总共用时{}秒".format((end - start)))

>>> 开始子进程1
    开始子进程2
    结果: 1152921504606846976
    结果: 1152921504606846976
    结束子进程2
    结束子进程1
    总共用时2.0016472339630127

3.使用线程锁

一个进程所含的不同线程间共享内存,这就意味着任何一个变量都可以被任何一个线程修改,因此线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。如果不同线程间有共享的变量,其中一个方法就是在修改前给其上一把锁lock,确保一次只有一个线程能修改它。threading.lock()方法可以轻易实现对一个共享变量的锁定,修改完后release供其它线程使用

import threading

class Account:
    def __init__(self):
        self.balance = 0

    def add(self, lock):
        # 获得锁
        lock.acquire()
        for i in range(0, 100000):
            self.balance += 1
        # 释放锁
        lock.release()

    def delete(self, lock):
        # 获得锁
        lock.acquire()
        for i in range(0, 100000):
            self.balance -= 1
            # 释放锁
        lock.release()

if __name__ == "__main__":
    account = Account()
    lock = threading.Lock()
    # 创建线程
    thread_add = threading.Thread(target=account.add, args=(lock,), name='Add')
    thread_delete = threading.Thread(target=account.delete, args=(lock,), name='Delete')

    # 启动线程
    thread_add.start()
    thread_delete.start()

    # 等待线程结束
    thread_add.join()
    thread_delete.join()

    print('The final balance is: {}'.format(account.balance))

>>>The final balance is: 0

多进程

Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多

1.Process实例化实现

from multiprocessing import Process

def fun1(name):
    print('测试%s多进程' %name)

if __name__ == '__main__':
    process_list = []
    for i in range(5):  #开启5个子进程执行fun1函数
        p = Process(target=fun1,args=('Python',)) #实例化进程对象
        p.start()
        process_list.append(p)
    for i in process_list:
        p.join()
    print('结束测试')

>>> 测试Python多进程
    测试Python多进程
    测试Python多进程
    测试Python多进程
    测试Python多进程
    结束测试

可以看到结果差不多是同时打印的,实现了真正的并行操作,就是多个CPU同时执行任务

2.继承Process类创建新进程

from multiprocessing import  Process

class MyProcess(Process): #继承Process类
    def __init__(self,name):
        super(MyProcess,self).__init__()
        self.name = name

    def run(self):
        print('测试%s多进程' % self.name)

if __name__ == '__main__':
    process_list = []
    for i in range(5):  #开启5个子进程执行fun1函数
        p = MyProcess('Python') #实例化进程对象
        p.start()
        process_list.append(p)
    for i in process_list:
        p.join()
    print('结束测试')

3.进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。就是固定有几个进程可以使用。

from  multiprocessing import Process,Pool
import os, time, random

def fun1(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    pool = Pool(5) #创建一个5个进程的进程池
    for i in range(10):
        pool.apply_async(func=fun1, args=(i,))
    pool.close()
    pool.join()
    print('结束测试')

版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:http://example.com/article/python_thread/