• 售前

  • 售后

热门帖子
入门百科

python 多进程和多线程使用详解

[复制链接]
创新2017 显示全部楼层 发表于 2021-10-26 14:25:07 |阅读模式 打印 上一主题 下一主题
目次


  • 进程和线程
  • Python的多进程
  • 进程池
  • 多进程间的数据通信与共享
  • Python的多线程
  • 多线程间的数据共享

    • 使用queue队列通信-经典的生产者和消耗者模子


进程和线程


进程是体系举行资源分配的最小单元,线程是体系举行调理实行的最小单元;
一个应用步伐至少包含一个进程,一个进程至少包含一个线程;
每个进程在实行过程中拥有独立的内存空间,而一个进程中的线程之间是共享该进程的内存空间的;
      
  • 盘算机的核心是CPU,它负担了所有的盘算使命。它就像一座工厂,时刻在运行。  
  • 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时间,其他车间都必须歇工。背后的含义就是,单个CPU一次只能运行一个使命。编者注: 多核的CPU就像有了多个发电厂,使多工厂(多进程)实现可能。  
  • 进程就好比工厂的车间,它代表CPU所能处置惩罚的单个使命。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。  
  • 一个车间里,可以有很多工人。他们协同完成一个使命。  
  • 线程就好比车间里的工人。一个进程可以包罗多个线程。  
  • 车间的空间是工人们共享的,好比许多房间是每个工人都可以收支的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。  
  • 可是,每间房间的巨细差别,有些房间最多只能容纳一个人,好比厕所。里面有人的时间,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它竣事,才气使用这一块内存。  
  • 一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。  
  • 还有些房间,可以同时容纳n个人,好比厨房。也就是说,假如人数大于n,多出来的人只能在外貌等着。这好比某些内存区域,只能供给固定命目的线程使用。  
  • 这时的办理方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来包管多个线程不会互相冲突。  
  • 不难看出,mutex是semaphore的一种特别情况(n=1时)。也就是说,完全可以用后者更换前者。但是,因为mutex较为简单,且效率高,所以在必须包管资源独占的情况下,还是采用这种筹划。

Python的多进程


Python的多进程依靠于multiprocess模块;使用多进程可以利用多个CPU举行并行盘算;
实例:
  1. from multiprocessing import Process
  2. import os
  3. import time
  4.  
  5. def long_time_task(i):
  6.     print('子进程: {} - 任务{}'.format(os.getpid(), i))
  7.     time.sleep(2)
  8.     print("结果: {}".format(8 ** 20))
  9.  
  10. if __name__=='__main__':
  11.     print('当前母进程: {}'.format(os.getpid()))
  12.     start = time.time()
  13.     p1 = Process(target=long_time_task, args=(1,))
  14.     p2 = Process(target=long_time_task, args=(2,))
  15.     print('等待所有子进程完成。')
  16.     p1.start()
  17.     p2.start()
  18.     p1.join()
  19.     p2.join()
  20.     end = time.time()
  21.     print("总共用时{}秒".format((end - start)))
复制代码
新创建进程和进程间切换是需要消耗资源的,所以应该控制进程数量;
同时可运行的进程数量收到CPU核数限定;

进程池


使用进程池pool创建进程:
使用进程池可以制止手工举行进程的创建的麻烦,默认数量是CPU核数;
Pool类可以提供指定命量的进程供用户使用,当有新的请求被提交到Pool中的时间,假如进程池还没有满,就会创建一个新的进程来实行请求;假如池已经满了,请求就会期待,比及有空闲进程可以使用时,才会实行请求;
几个方法:
1.apply_async
作用是向进程池提交需要实行的函数和参数,各个进程采用非阻塞的异步方式调用,每个进程只管本身运行,是默认方式;
2.map
会阻塞进程直到返回效果;
3.map_sunc
非阻塞进程;
4.close
关闭进程池,不再担当使命;
5.terminate
竣事进程;
6.join
主进程阻塞,直到子进程实行竣事;
实例:
  1. from multiprocessing import Pool, cpu_count
  2. import os
  3. import time
  4.  
  5. def long_time_task(i):
  6.     print('子进程: {} - 任务{}'.format(os.getpid(), i))
  7.     time.sleep(2)
  8.     print("结果: {}".format(8 ** 20))
  9.  
  10. if __name__=='__main__':
  11.     print("CPU内核数:{}".format(cpu_count()))
  12.     print('当前母进程: {}'.format(os.getpid()))
  13.     start = time.time()
  14.     p = Pool(4)
  15.     for i in range(5):
  16.         p.apply_async(long_time_task, args=(i,))
  17.     print('等待所有子进程完成。')
  18.     p.close()
  19.     p.join()
  20.     end = time.time()
  21.     print("总共用时{}秒".format((end - start)))
复制代码
在join之前,必须使用close或者terminate,让进程池不再担当使命;

多进程间的数据通信与共享


通常,进程之间是相互独立的,每个进程都有独立的内存。通过共享内存(nmap模块),进程之间可以共享对象,使多个进程可以访问同一个变量(所在雷同,变量名可能差别)。多进程共享资源一定会导致进程间相互竞争,所以应该尽最大可能防止使用共享状态。还有一种方式就是使用队列queue来实现差别进程间的通信或数据共享,这一点和多线程编程雷同。
下例这段代码中中创建了2个独立进程,一个负责写(pw), 一个负责读(pr), 实现了共享一个队列queue。
  1. from multiprocessing import Process, Queue
  2. import os, time, random
  3.  
  4. # 写数据进程执行的代码:
  5. def write(q):
  6.     print('Process to write: {}'.format(os.getpid()))
  7.     for value in ['A', 'B', 'C']:
  8.         print('Put %s to queue...' % value)
  9.         q.put(value)
  10.         time.sleep(random.random())
  11.  
  12. # 读数据进程执行的代码:
  13. def read(q):
  14.     print('Process to read:{}'.format(os.getpid()))
  15.     while True:
  16.         value = q.get(True)
  17.         print('Get %s from queue.' % value)
  18.  
  19. if __name__=='__main__':
  20.     # 父进程创建Queue,并传给各个子进程:
  21.     q = Queue()
  22.     pw = Process(target=write, args=(q,))
  23.     pr = Process(target=read, args=(q,))
  24.     # 启动子进程pw,写入:
  25.     pw.start()
  26.     # 启动子进程pr,读取:
  27.     pr.start()
  28.     # 等待pw结束:
  29.     pw.join()
  30.     # pr进程里是死循环,无法等待其结束,只能强行终止:
  31.     pr.terminate()
复制代码
Python的多线程


python 3中的多进程编程主要依靠threading模块。创建新线程与创建新进程的方法非常雷同。threading.Thread方法可以接收两个参数, 第一个是target,一样寻常指向函数名,第二个时args,需要向函数通报的参数。对于创建的新线程,调用start()方法即可让其开始。我们还可以使用current_thread().name打印出当火线程的名字。
  1. import threading
  2. import time
  3.  
  4. def long_time_task(i):
  5.     print('当前子线程: {} 任务{}'.format(threading.current_thread().name, i))
  6.     time.sleep(2)
  7.     print("结果: {}".format(8 ** 20))
  8.  
  9. if __name__=='__main__':
  10.     start = time.time()
  11.     print('这是主线程:{}'.format(threading.current_thread().name))
  12.     thread_list = []
  13.     for i in range(1, 3):
  14.         t = threading.Thread(target=long_time_task, args=(i, ))
  15.         thread_list.append(t)
  16.     for t in thread_list:
  17.         t.start()
  18.     for t in thread_list:
  19.         t.join()
  20.     end = time.time()
  21.     print("总共用时{}秒".format((end - start)))
复制代码
多线程间的数据共享


一个进程所含的差别线程间共享内存,这就意味着任何一个变量都可以被任何一个线程修改,因此线程之间共享数据最大的伤害在于多个线程同时改一个变量,把内容给改乱了。假如差别线程间有共享的变量,其中一个方法就是在修改前给其上一把锁lock,确保一次只有一个线程能修改它。threading.lock()方法可以轻易实现对一个共享变量的锁定,修改完后release供别的线程使用。
  1. import threading
  2.  
  3. class Account:
  4.     def __init__(self):
  5.         self.balance = 0
  6.  
  7.     def add(self, lock):
  8.         # 获得锁
  9.         lock.acquire()
  10.         for i in range(0, 100000):
  11.             self.balance += 1
  12.         # 释放锁
  13.         lock.release()
  14.  
  15.     def delete(self, lock):
  16.         # 获得锁
  17.         lock.acquire()
  18.         for i in range(0, 100000):
  19.             self.balance -= 1
  20.             # 释放锁
  21.         lock.release()
  22.  
  23. if __name__ == "__main__":
  24.     account = Account()
  25.     lock = threading.Lock()
  26.     # 创建线程
  27.    thread_add = threading.Thread(target=account.add, args=(lock,), name='Add')
  28.     thread_delete = threading.Thread(target=account.delete, args=(lock,), name='Delete')
  29.  
  30.     # 启动线程
  31.    thread_add.start()
  32.     thread_delete.start()
  33.  
  34.     # 等待线程结束
  35.    thread_add.join()
  36.     thread_delete.join()
  37.  
  38.     print('The final balance is: {}'.format(account.balance))
复制代码
使用queue队列通信-经典的生产者和消耗者模子
  1. from queue import Queue
  2. import random, threading, time
  3.  
  4. # 生产者类
  5. class Producer(threading.Thread):
  6.     def __init__(self, name, queue):
  7.         threading.Thread.__init__(self, name=name)
  8.         self.queue = queue
  9.  
  10.     def run(self):
  11.         for i in range(1, 5):
  12.             print("{} is producing {} to the queue!".format(self.getName(), i))
  13.             self.queue.put(i)
  14.             time.sleep(random.randrange(10) / 5)
  15.         print("%s finished!" % self.getName())
  16.  
  17. # 消费者类
  18. class Consumer(threading.Thread):
  19.     def __init__(self, name, queue):
  20.         threading.Thread.__init__(self, name=name)
  21.         self.queue = queue
  22.  
  23.     def run(self):
  24.         for i in range(1, 5):
  25.             val = self.queue.get()
  26.             print("{} is consuming {} in the queue.".format(self.getName(), val))
  27.             time.sleep(random.randrange(10))
  28.         print("%s finished!" % self.getName())
  29.  
  30. def main():
  31.     queue = Queue()
  32.     producer = Producer('Producer', queue)
  33.     consumer = Consumer('Consumer', queue)
  34.  
  35.     producer.start()
  36.     consumer.start()
  37.  
  38.     producer.join()
  39.     consumer.join()
  40.     print('All threads finished!')
  41.  
  42. if __name__ == '__main__':
  43.     main()
复制代码
      
  • 对CPU密集型代码(好比循环盘算) - 多进程效率更高  
  • 对IO密集型代码(好比文件操作,网络爬虫) - 多线程效率更高。
对于IO密集型操作,大部分消耗时间实在是期待时间,在期待时间中CPU是不需要工作的,那你在此期间提供双CPU资源也是利用不上的,相反对于CPU密集型代码,2个CPU干活肯定比一个CPU快很多。那么为什么多线程会对IO密集型代码有效呢?这时因为python碰到期待会开释GIL供新的线程使用,实现了线程间的切换。
以上就是python 多进程和多线程使用详解的具体内容,更多关于python 多进程和多线程的资料请关注草根技术分享别的相干文章!

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作