1 Python中如何使用线程池和进程池?
需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池。其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉
只要你用并发,就会有锁的问题,但是你不能一直去自己加锁吧 那么我们就用QUEUE,这样还解决了自动加锁的问题 由Queue延伸出的一个点也非常重要的概念。以后写程序也会用到
这个思想。就是生产者与消费者问题
Python标准模块--concurrent.futures(并发未来)
concurent.future模块需要了解的
1.concurent.future模块是用来创建并行的任务,提供了更高级别的接口,为了异步执行调用2.concurent.future这个模块用起来非常方便,它的接口也封装的非常简单3.concurent.future模块既可以实现进程池,也可以实现线程池4.模块导入进程池和线程池from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor还可以导入一个Executor,但是你别这样导,这个类是一个抽象类抽象类的目的是规范他的子类必须有某种方法(并且抽象类的方法必须实现),但是抽象类不能被实例化5. p = ProcessPoolExecutor(max_works)对于进程池如果不写max_works:默认的是cpu的数目,默认是4个 p = ThreadPoolExecutor(max_works)对于线程池如果不写max_works:默认的是cpu的数目*56.如果是进程池,得到的结果如果是一个对象。我们得用一个.get()方法得到结果 但是现在用了concurent.future模块,我们可以用obj.result方法 p.submit(task,i) #相当于apply_async异步方法 p.shutdown() #默认有个参数wite=True (相当于close和join)复制代码
那么什么是线程池呢?我们来了解一下
线程池:就是在一个进程内控制一定个数的线程
基于concurent.future模块的进程池和线程池 (他们的同步执行和异步执行是一样的)
基于concurrent.futures模块的进程池
# 1.同步执行--------------from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutorimport os,time,randomdef task(n): print('[%s] is running'%os.getpid()) time.sleep(random.randint(1,3)) #I/O密集型的,,一般用线程,用了进程耗时长 return n**2if __name__ == '__main__': start = time.time() p = ProcessPoolExecutor() for i in range(10): #现在是开了10个任务, 那么如果是上百个任务呢,就不能无线的开进程,那么就得考虑控制 # 线程数了,那么就得考虑到池了 obj = p.submit(task,i).result() #相当于apply同步方法 p.shutdown() #相当于close和join方法 print('='*30) print(time.time() - start) #17.36499309539795复制代码
# 2.异步执行-----------# from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor# import os,time,random# def task(n):# print('[%s] is running'%os.getpid())# time.sleep(random.randint(1,3)) #I/O密集型的,,一般用线程,用了进程耗时长# return n**2# if __name__ == '__main__':# start = time.time()# p = ProcessPoolExecutor()# l = []# for i in range(10): #现在是开了10个任务, 那么如果是上百个任务呢,就不能无线的开进程,那么就得考虑控制# # 线程数了,那么就得考虑到池了# obj = p.submit(task,i) #相当于apply_async()异步方法# l.append(obj)# p.shutdown() #相当于close和join方法# print('='*30)# print([obj.result() for obj in l])# print(time.time() - start) #5.362306594848633复制代码
基于concurrent.futures模块的线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutorfrom threading import currentThreadimport os,time,randomdef task(n): print('%s:%s is running'%(currentThread().getName(),os.getpid())) #看到的pid都是一样的,因为线程是共享了一个进程 time.sleep(random.randint(1,3)) #I/O密集型的,,一般用线程,用了进程耗时长 return n**2if __name__ == '__main__': start = time.time() p = ThreadPoolExecutor() #线程池 #如果不给定值,默认cup*5 l = [] for i in range(10): #10个任务 # 线程池效率高了 obj = p.submit(task,i) #相当于apply_async异步方法 l.append(obj) p.shutdown() #默认有个参数wite=True (相当于close和join) print('='*30) print([obj.result() for obj in l]) print(time.time() - start) #3.001171827316284复制代码
map函数的应用
# map函数举例obj= map(lambda x:x**2 ,range(10))print(list(obj))#运行结果[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]复制代码
应用线程池(下载网页并解析)
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutorimport requestsimport time,osdef get_page(url): print('<%s> is getting [%s]'%(os.getpid(),url)) response = requests.get(url) if response.status_code==200: #200代表状态:下载成功了 return { 'url':url,'text':response.text}def parse_page(res): res = res.result() print('<%s> is getting [%s]'%(os.getpid(),res['url'])) with open('db.txt','a') as f: parse_res = 'url:%s size:%s\n'%(res['url'],len(res['text'])) f.write(parse_res)if __name__ == '__main__': # p = ThreadPoolExecutor() p = ProcessPoolExecutor() l = [ 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com', 'http://www.baidu.com', ] for url in l: res = p.submit(get_page,url).add_done_callback(parse_page) #这里的回调函数拿到的是一个对象。得 # 先把返回的res得到一个结果。即在前面加上一个res.result() #谁好了谁去掉回调函数 # 回调函数也是一种编程思想。不仅开线程池用,开线程池也用 p.shutdown() #相当于进程池里的close和join print('主',os.getpid())复制代码
可以和上面的开进程池/线程池的对比着看,就能发现map函数的强大了
# 我们的那个p.submit(task,i)和map函数的原理类似。我们就# 可以用map函数去代替。更减缩了代码from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutorimport os,time,randomdef task(n): print('[%s] is running'%os.getpid()) time.sleep(random.randint(1,3)) #I/O密集型的,,一般用线程,用了进程耗时长 return n**2if __name__ == '__main__': p = ProcessPoolExecutor() obj = p.map(task,range(10)) p.shutdown() #相当于close和join方法 print('='*30) print(obj) #返回的是一个迭代器 print(list(obj))复制代码
2 threading.local的作用?
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。
Threading.local可以创建一个对象,每个线程都可以对他读写属性,但不会互相影响
import threadingimport time# 创建全局ThreadLocal对象:local_school = threading.local()def process_student(): print('Hello, %s (in %s)' % (local_school.student, threading.current_thread().name))def process_thread(name): # 绑定ThreadLocal的student: local_school.student = name time.sleep(2) process_student()a = []for i in range(20): a.append(threading.Thread(target= process_thread, args=(str(i),), name=str(i)))for i in a: i.start()复制代码
通过字典以及面向对象中的魔法方法来自己实现一个
import timefrom threading import get_ident,Threadclass PPP: def __init__(self): object.__setattr__(self,"storage", {}) def __setattr__(self, key, value): if get_ident() in self.storage: self.storage[get_ident()][key]=value else: self.storage[get_ident()] ={key: value} def __getattr__(self, item): return self.storage[get_ident()][item]p =PPP()def task(arg): p.a = arg time.sleep(2) print(p.a)for i in range(10): t = Thread(target=task,args=(i,)) t.start()复制代码
3 并发和并行有什么区别?
做并发编程之前,必须首先理解什么是并发,什么是并行,什么是并发编程,什么是并行编程。
并发(concurrency)和并行(parallellism)是:
解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
解释三:在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
4 路由器与交换机的区别
一、工作所在的OSI层次不一样(根本区别,导致接下来的区别) 交换机工作在 OSI模型的数据链路层,所以工作原理比较简单; 路由器工作在OSI模型的网络层,具备更多的网络协议信息,所以可以做出更好的数据转发策略。
二、数据转发所依据的对象也不一样。 交换机工作在数据链路层,所以交换机转发数据依靠的是每个物理地址(MAC地址),MAC地址一般是设备生产商在设备出厂时固定在设备中的,不能进行更改。 路由器工作在网络层,所以其交换数据依靠网络地址(IP地址),而IP地址是由网络管理员自己分配或者系统自动获取的。
三、是否可以分割广播域 由交换机连接的所有端口仍然属于同一个广播域,所以极有可能会产生数据拥堵; 连接到路由器上的所有端口不在属于同一个广播域,所以不会产生类似的数据拥堵问题。
5 如何修改本地hosts文件?
Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
浏览器访问网站,要首先通过DNS服务器把要访问的网站域名解析成一个唯一的IP地址,之后,浏览器才能对此网站进行定位并且访问其数据。
操作系统规定,在进行DNS请求以前,先检查系自己的Hosts文件中是否有这个域名和IP的映射关系。如果有,则直接访问这个IP地址指定的网络位置,如果没有,再向已知的DNS服务器提出域名解析请求。也就是说Hosts的IP解析优先级比DNS要高。
hosts文件原内容如下:
# Copyright (c) 1993-2009 Microsoft Corp.## This is a sample HOSTS file used by Microsoft TCP/IP for Windows.## This file contains the mappings of IP addresses to host names. Each# entry should be kept on an individual line. The IP address should# be placed in the first column followed by the corresponding host name.# The IP address and the host name should be separated by at least one# space.## Additionally, comments (such as these) may be inserted on individual# lines or following the machine name denoted by a '#' symbol.## For example:## 102.54.94.97 rhino.acme.com # source server# 38.25.63.10 x.acme.com # x client host# localhost name resolution is handled within DNS itself.# 127.0.0.1 localhost# ::1 localhost复制代码
在最后新增如下内容:
127.0.0.1 www.163.com
表示域名“www.163.com”指向的ip为127.0.0.1,即本地。修改后文件内容如下:
# Copyright (c) 1993-2009 Microsoft Corp.## This is a sample HOSTS file used by Microsoft TCP/IP for Windows.## This file contains the mappings of IP addresses to host names. Each# entry should be kept on an individual line. The IP address should# be placed in the first column followed by the corresponding host name.# The IP address and the host name should be separated by at least one# space.## Additionally, comments (such as these) may be inserted on individual# lines or following the machine name denoted by a '#' symbol.## For example:## 102.54.94.97 rhino.acme.com # source server# 38.25.63.10 x.acme.com # x client host# localhost name resolution is handled within DNS itself.# 127.0.0.1 localhost# ::1 localhost127.0.0.1 www.163.com复制代码
修改后用浏览器访问“www.163.com”会被解析到127.0.0.1,导致无法显示该网页。