不错的免费网站建设,网站跟自媒体建设,那个网站有用director做的片头,wordpress主题 推荐Python线程和队列使用的一点思考1. 斗哥采访环节请问为什么要使用线程#xff1f;答#xff1a;为了提高程序速度#xff0c;代码效率呀。请问为什么要使用队列#xff1f;答#xff1a;个人认为队列可以保证线程安全#xff0c;实现线程间的同步#xff0c;比较稳。线程…Python线程和队列使用的一点思考1. 斗哥采访环节请问为什么要使用线程答为了提高程序速度代码效率呀。请问为什么要使用队列答个人认为队列可以保证线程安全实现线程间的同步比较稳。线程为什么采用Threading模块答据我所知还有Thread模块该模块级别较低不推荐用。更高级别的是threading模块它有一个Thread类而且提供了各种非常好用的同步机制。你所说的同步机制是指啥答就是希望线程能够同时开跑想象一下“所有的马同时冲出栅栏”的场景就是我们说的同步了而Therad模块的同步机制不佳亦是其不推荐使用的原因之一。2. 需要用到线程的场景2.1 举个简单的案例假设这么一个需求如下给定200个IP地址可能开放端口有8044370017002800080808081888890009001等现需以[[http://ip:port](http://ip:port)]([http://ip:port](http://ip:port))形式访问页面以判断是否正常。2.2 为什么要用线程解决这个需求200个ip地址和10个端口累计请求2000次一个个请求过去太慢设定线程可以提高效率。2.3 如果不用线程怎么样实现(以下仅为演示代码如有错误敬请指出)**注**将200个ip地址放到ip.txt记事本中读取ip拼接端口并请求。#-*-coding:utf-8import requestsportlist[80,443,7001,7002,8000,8080,8081,8888,9000,9001]ips[t.replace(\n,) for t in open(ip.txt,r).readlines()]for ip in ips:for port in portlist:urlhttp://ip:str(port)try:resprequests.get(urlurl,timeout2)print url,mabey normal...except:print url,unknown wrong...注运行上述代码请求2000条url每条等待超时2秒差不多要1个多小时才能跑完漫长的等待过程中渐渐失去笑容和耐心……3. threading如何运用以解决上述问题使用threading模块的Thread类来创建线程先要创建一个Thread的实例传给它一个函数去跑线程。比如专门定义一个函数req()来请求URL然后把这个req函数传给Thread的实例接着开启线程……可以先看下面这段代码。(以下代码修改自上文)import requestsimport threadingdef req(url): #请求的代码写成一个函数try:resprequests.get(urlurl,timeout2)print url,mabey normal...except:print url,unknown wrong...def main():portlist[80,443,7001,7002,8000,8080,8081,8888,9000,9001]ips[t.replace(\n,) for t in open(ip.txt,r).readlines()]urllist[]threads[]for ip in ips: #将url写到列表中for port in portlist:urllist.append(http://ip:str(port))for url in urllist: #将线程存到threads列表中tthreading.Thread(targetreq,args(url,))threads.append(t)for t in threads: #开始跑线程用while来控制线程数t.start()while True:if(len(threading.enumerate())100):breakif __name__ __main__:main()其中 tthreading.Thread(targetreq,args(url,))的t就是一个Thread的实例了args是可以加入到函数传递的参数而本代码的req()函数需要传递参数是url。你可以看到的是这个代码建立了2000个未开始跑的线程放到threads列表里接着遍历threads来开启线程。为了防止线程数过多用while循环判断如果当前线程数len(threading.enumerate()超过了100则不开启下一个线程也就是100指的是线程数。3.1 简单评价下这个脚本(有其他建议请留言评论)代码效果线程设置成100不到1分钟时间就跑完了整个脚本。为了方便将url写到了列表里付出的代价是浪费了相应的内存空间。线程数的控制使用while循环和threading.enumerate()来判断不够优雅。3.2 更好一点的方式使用for循环来控制线程数while循环结合列表的pop方法import requestsimport threadingdef req():while True:try:urlurllist.pop()except IndexError:breaktry:resprequests.get(urlurl,timeout2)print url,mabey normal...except:print url,unknown wrong...def main():for i in range(10):tthreading.Thread(targetreq)t.start()for i in range(10):t.join()if __name__ __main__:portlist[80,443,7001,7002,8000,8080,8081,8888,9000,9001]ips[t.replace(\n,) for t in open(ip.txt,r).readlines()]urllist[]for ip in ips:for port in portlist:urllist.append(http://ip:str(port))main()你可以发现上述代码大概有2点变化。线程的开启更加纯粹不再有传递参数的功能。而多了个for循环来执行t.join()这个是用来阻塞主线程当开启的子线程未跑完时主线程不往下继续执行。参数url的获取改成了urlurllist.pop()的方式因为我们知道列表的pop方法会默认每次从列表移除最后一个元素并返回该元素的值所以能够起到参数获取的作用。线程数的控制用for i in range(10)来开启而不用while循环不停去检测线程数是不是超了。而参数获取完成了之后列表也空了似乎达到节省了空间不过我们还是得事先准备一个列表把url一个个预先填进去(如下图)。如果不希望暂用那么大的空间那么我们需要有一个缓存空间并发的存入且能够并发读取而且不会发生阻塞脑补一张图大概长下面这样上图描述就是人们常说的做生产者和消费者模式。在python中Queue模块实现了多生产者多消费者队列, 尤其适合多线程编程.Queue类中实现了所有需要的锁原语可以优雅的解决上述的问题那么首先需要了解一下关于队列的一些细节……4. 队列几点介绍4.1 导入import Queuefrom Queue import [Queue Class]4.2 通用方法put(item(,block[,timeout]))从队列中放入item。get()从队列移除并返回一个数据。(这个方法和列表的pop()方法是不是很像)empty()如果队列为空返回True,反之返回Falsetask_done()task_done()告诉队列get()方法的任务处理完毕。join()阻塞调用线程直到队列中的所有任务被处理掉。4.3 队列模型(类)FIFO队列(First in First Out先进先出)class Queue.Queue(maxsize0)Queue提供了一个基本的FIFO容器maxsize是个整数指明了队列中能存放的数据个数的上限。一旦达到上限插入会导致阻塞直到队列中的数据被消费掉。如果maxsize小于或者等于0队列大小没有限制。import QueueqQueue.Queuefor i in range(1,6):q.put(i)while not q.empty():print q.get()[console]$ python queth.py12345更多用法参考官方文档Queue官方文档4.4 多线程和Queue.Queue()前面已经提到参数的获取可以并发的实现但是苦于一直没有找到合适的场景。我们在文章中提到的需求你可以发现2000个url的获取通过个循环就可以轻易获取根本用不到生产者的模式也就提现不出队列的强大尽管如此我还是给出对应的脚本你可以发现其实和用列表获取参数的差别并不大。(小伙伴有更好的场景欢迎提出来一起讨论呀)import requestsimport threadingfrom Queue import Queuedef req(queue):while True:urlqueue.get()try:resprequests.get(urlurl,timeout2)queue.task_done()print url,mabey normal...except:print url,unknown wrong...queue.task_done()def get_url(queue):portlist[80,443,7001,7002,8000,8080,8081,8888,9000,9001]ips[t.replace(\n,) for t in open(ip.txt,r).readlines()]for ip in ips:for port in portlist:urlhttp://ip:str(port)queue.put(url,1)def main():queueQueue()get_url(queue)for i in range(10):tthreading.Thread(targetreq,args(queue,))t.setDaemon(True)t.start()queue.join()if __name__ __main__:main()你可以发现通过一个get_url()函数就轻易将url存储到队列中我们在定义queue的时候是可以设定队列空间大小的如queueQueue(100)当存放了100个元素而未被取走时队列会处于阻塞状态。不过设定队列大小上述代码就需要改写了可以参考《Python核心编程》关于线程和队列的章节。5. 小结以上就是本次关于线程和队列思考的全部内容了希望能够帮助到那些刚入门python的新手玩家们。本文也仅限斗哥的一点点小思考也希望大家能够提出更好的见解和斗哥一起讨论。(The End)