你知道吗?


在我的心里


你是多么的重要


就像



请允许我来一段 freestyle


你们准备好了妹油


你看


这个碗



它又大又圆


就像


这条面



它又长又宽


你们


在这里


看文章


觉得 很开心


就像 


我在这里


给你们


写文章


觉得很开心


skr~~




不好意思


走错片场了


ok..


接下来,就是


学习 python 的正确姿势




咱们在上一次的


python爬虫13 | 秒爬,这多线程爬取速度也太猛了,这次就是要让你的爬虫效率杠杠的


了解了一些 python 高效爬虫的概念


比如多线程、多进程、协程等


那么我们这一篇就开始了解多线程的具体使用


在 python 中


常用的多线程的模块有这么几个


_thread

threading

Queue


之前有个 thread 模块


被 python3 抛弃了


改名为  _thread


但其实 _thread 也没什么人用


因为 _thread 有的 threading 都有


_thread 没有的 threading 依然有



那么接下来我们就先来玩玩 threading 


在此之前


(请允许小帅b又开始吹水了~)


介绍一下 小帅b 的一点背景


小帅b呢


平常上班时间都会去河边摸鱼


每天得摸 20 条鱼



一条一条的摸


为的是什么


为的是安抚这些鱼的心情


这样以后送到餐前的红烧鱼才更加美味





小帅b每天得摸 20 条鱼


每隔一秒钟摸一条


也就是这样


import time

def moyu_time(name, delay, counter):
 while counter:
   time.sleep(delay)
   print("%s 开始摸鱼 %s" % (name, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
   counter -= 1


if __name__ == '__main__':
 moyu_time('小帅b',1,20)

 


后来


小帅b知道了多线程


拍脑一想


我靠


应该把小明和小红拉过来


让他们一起帮我摸鱼啊


也就是让小明和小红同时一人摸 10 条鱼


想想就开心



在 小帅b 的威逼利诱下


他们俩不情愿的手拉着手来到了河边


小帅b看小红是女生


就让小红每摸一条鱼休息 2 秒钟


而小明每摸一条鱼休息 1 秒钟


先扔一段代码给你


# encoding = utf-8

import threading
import time


# 创建一个线程子类
class MyThread(threading.Thread):
  def __init__(self,threadID, name, counter):
    threading.Thread.__init__(self)
    self.threadID = threadID
    self.name = name
    self.counter = counter

  def run(self):
    print("开始线程:" + self.name)
    moyu_time(self.name, self.counter, 10)
    print("退出线程:" + self.name)

def moyu_time(threadName, delay, counter):
  while counter:
    time.sleep(delay)
    print("%s 开始摸鱼 %s" % (threadName, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
    counter -= 1


# 创建新线程
# 小帅b找了两个人来摸鱼
# 让小明摸一次鱼休息1秒钟
# 让小红摸一次鱼休息2秒钟
thread1 = MyThread(1"小明"1)
thread2 = MyThread(2"小红"2)

# 开启新线程
thread1.start()
thread2.start()
# 等待至线程中止
thread1.join()
thread2.join()
print ("退出主线程")


在这里呢


我们创建了一个线程类


然后继承 threading.Thread


在我们这个线程类里面定义了一个 run 方法


这个 run 方法去调用了摸鱼的方法


可以看到我们创建了两个线程


一个叫小明线程


一个叫小红线程


thread1 = MyThread(1"小明"1)
thread2 = MyThread(2"小红"2)



当我们的线程调用 start 方法的时候


它们就会去执行 run 方法


而我们用到的 join 方法呢


是为了让线程执行完


再终止主程序



运行一下就是这样


开始线程:小明
开始线程:小红
小明 开始摸鱼 2019-03-10 23:15:26
小红 开始摸鱼 2019-03-10 23:15:27
小明 开始摸鱼 2019-03-10 23:15:27
小明 开始摸鱼 2019-03-10 23:15:28
小红 开始摸鱼 2019-03-10 23:15:29
小明 开始摸鱼 2019-03-10 23:15:29
小明 开始摸鱼 2019-03-10 23:15:30
小明 开始摸鱼 2019-03-10 23:15:31
小红 开始摸鱼 2019-03-10 23:15:31
小明 开始摸鱼 2019-03-10 23:15:32
小明 开始摸鱼 2019-03-10 23:15:33
小红 开始摸鱼 2019-03-10 23:15:33
小明 开始摸鱼 2019-03-10 23:15:34
小红 开始摸鱼 2019-03-10 23:15:35
小明 开始摸鱼 2019-03-10 23:15:35
退出线程:小明
小红 开始摸鱼 2019-03-10 23:15:37
小红 开始摸鱼 2019-03-10 23:15:39
小红 开始摸鱼 2019-03-10 23:15:41
小红 开始摸鱼 2019-03-10 23:15:43
小红 开始摸鱼 2019-03-10 23:15:45
退出线程:小红
退出主线程

Process finished with exit code 0



小帅b再也不用摸鱼了


后来小明和小红都不乐意了


凭什么就我们两个摸鱼



这时候 小帅b 只能去找更多人了


连 小帅b 家的狗都叫过来了


然后


就疯狂的开启线程


thread1 = MyThread(1"小明"1)
thread2 = MyThread(2"小红"2)
thread3 = MyThread(3"小黄"2)
thread4 = MyThread(4"小绿"2)
...
thread5 = MyThread(55"小青"2)
thread6 = MyThread(56"小白"2)
thread7 = MyThread(57"小狗"2)



stop!!!



这可不行


因为频繁的创建线程 销毁线程


非常的浪费资源


所以呢


应该把他们放到池子里面去一起洗澡



哈,也就是


线程池


通过线程池就可以重复利用线程


不会造成过多的浪费


在 python 中


可以使用 ThreadPoolExecutor 来实现线程池


我们来往池子里塞 20 个线程


然后在循环的时候每次拿一个线程来摸鱼


def moyu_time(name, delay, counter):
  while counter:
    time.sleep(delay)
    print("%s 开始摸鱼 %s" % (name, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
    counter -= 1


if __name__ == '__main__':
  pool = ThreadPoolExecutor(20)
  for i in range(1,5):
    pool.submit(moyu_time('xiaoshuaib'+str(i),1,3))


运行一下


xiaoshuaib1 开始摸鱼 2019-03-10 23:30:10
xiaoshuaib1 开始摸鱼 2019-03-10 23:30:11
xiaoshuaib1 开始摸鱼 2019-03-10 23:30:12
xiaoshuaib2 开始摸鱼 2019-03-10 23:30:13
xiaoshuaib2 开始摸鱼 2019-03-10 23:30:14
xiaoshuaib2 开始摸鱼 2019-03-10 23:30:15
xiaoshuaib3 开始摸鱼 2019-03-10 23:30:16
xiaoshuaib3 开始摸鱼 2019-03-10 23:30:17
xiaoshuaib3 开始摸鱼 2019-03-10 23:30:18
xiaoshuaib4 开始摸鱼 2019-03-10 23:30:19
xiaoshuaib4 开始摸鱼 2019-03-10 23:30:20
xiaoshuaib4 开始摸鱼 2019-03-10 23:30:21



可以看到


我们每次从线程池里面去拿一个线程来摸鱼


这样就不会去重复的创建销毁线程了


当然


我们还可以用一个叫做 Queue 的队列来创建线程池


队列嘛~


就是可以往里塞东西


也可以往里拉东西


所以我们在使用队列的时候


最常用的方法就是 put 和 get 了


还是拿摸鱼为例


我们创建一个长度为 6 的队列


接着根据队列的长度创建了线程


每个线程都让它们处于守护状态


也就是需要的时候


马上执行


def queue_pool():
  queue = Queue(6)
  for i in range(queue.maxsize):
    t = CustomThread(queue)
    t.setDaemon(True)
    t.start()


接着我们就可以用 put 方法


把我们想做的事情往队列里面塞


比如这里我们想要摸鱼


for i in range(20):
  queue.put(moyu)
queue.join()



def moyu():
  print(" 开始摸鱼 %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))


要执行的话就需要去队列里面取了


q_method = self.__queue.get()
q_method()
self.__queue.task_done()


完整代码如下


import threadingimport timefrom queue import Queue

class CustomThread(threading.Thread): def __init__(self, queue): threading.Thread.__init__(self) self.__queue = queue
def run(self): while True: q_method = self.__queue.get() q_method() self.__queue.task_done()
def moyu(): print(" 开始摸鱼 %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
def queue_pool(): queue = Queue(5) for i in range(queue.maxsize): t = CustomThread(queue) t.setDaemon(True) t.start()
for i in range(20): queue.put(moyu) queue.join()
if __name__ == '__main__': queue_pool()


本篇就到这里吧


ps:本来想接着写一下用多线程来爬取网站的,篇幅有限,咱们下一篇再见


peace




                                        

      点个好看啊~~(破音)