多线程
前言
我看了不止一个人说多线程是鸡肋,但是就依照我个人觉得多线程在一些小型的爬虫中还是可以显著的提高速度的,相比多进程来说应该还是挺简单的
使用多线程
继承threading.Thread
继承threading.Thread模块是一个很好的一个选择,就像java中也是可以继承类和实现接口一样,这都是很好的选择,下面我们来看看具体如何使用
class Mythread(threading.Thread):
def __init__(self,threadID,name,counter):
threading.Thread.__init__(self) #首先需要先保留原来threading.Thread中的初始化函数
self.threadID=threadID #重命名线程的ID
self.name=name #线程的名字
self.counter=counter #线程的数量
def run(self):
lock.acquire() #获取线程锁Lock
for i in range(10):
print "线程"+self.name+"开始运行"
lock.release() #释放线程锁Lock
if __name__ == '__main__':
lock=threading.Lock()
t1=Mythread(0,"thread-1",3)
t2=Mythread(1,"thread-2",3)
t1.start()
t2.start()
threads=[]
threads.append(t1)
threads.append(t2)
for t in threads:
t.join() #阻塞主线程,直至线程运行完毕才运行main线程的语句
print "线程运行结束"
需要注意的是,这种继承的方式有一个缺点,这个和java中继承来实现多线程是一样的,就是一个对象只能是对应一个线程,并不能一个对象被多个线程共享,下面我们将会介绍另外的一种方式
直接调用threading.Thread
上面我们说过继承的方式,但是我个人觉得对于一些比较小的爬虫还是有些繁琐的,因为总是需要重写run方法,现在我们来看看如何简化实现多线程
"""
这是一个简单的例子,其实也不是一个好的例子,但是为了演示方便就选用了,可以看出这里是直接调用
了func函数,然后变成多个线程同时并行,其中target是要调用的方法(没有括号),args是方法调用需要传入的参数
其实这个还是和上面的继承比较相似的
"""
def func(name,age):
for i in range(10):
print name+"的年龄为:"+str(age)
t=threading.Thread(target=func,args=["陈加兵",22])
t.start()
Thread对象的相关方法
- start() 启动线程
- join([timeout]) 设置阻塞线程,timeout是可选的参数,表示阻塞的时间,如果没有就是当此线程运行结束才开始运行下一个线程
- run() 线程活动的方法
- getName() 获取线程名称
- setName() 设置线程的名称
- isAlive() 判断线程是否还活着
- isDaemon() 判断是否是守护线程
- setDaemon() 设置为守护线程,守护线程就是当主线程运行完后,这个线程也会随着主线程的结束而结束
共享队列
从源代码可以看出队列是实现了锁原语的,因此可以使用队列实现线程的同步,这里的主要原理就不细说了,简单的说就是get和put等方法都实现了锁原语,就是当一个操作正在执行的时候其他的操作会阻塞等待
下面我自己写了一个使用两个线程实现同时入队和出队的程序
import random
import time
from Queue import Queue
class myThread(threading.Thread):
def __init__(self,threadID,name,counter,q,flag):
"""
threadID是线程的ID
name是线程的名称
q是先进先出队列
flag是用来调用get和put的标志
"""
threading.Thread.__init__(self)
self.name=name
self.threadID=threadID
self.counter=counter
self.q=q
self.flag=flag
def run(self):
"""
当flag为1时就调用put方法,否则调用get
"""
if self.flag==1:
self.put()
else:
self.get()
def put(self):
while True:
self.q.put(random.randint(0,10))
def get(self):
while True:
if not self.q.empty():
print self.q.get()
if __name__=="__main__":
threadLock=threading.Lock()
q=Queue()
t1=myThread(1,"Thread-1",1,q,1)
t2=myThread(2,"Thread-2",2,q,2)
threads=[]
threads.append(t1)
threads.append(t2)
t1.start()
t2.start()
Queue相关的一些方法
- Queue.qsize() 返回队列的大小
- Queue.empty() 如果队列为空,返回True,反之False
- Queue.full() 如果队列满了,返回True,反之False
- Queue.full 与 maxsize 大小对应
- Queue.get([block[, timeout]])获取队列,timeout等待时间
- Queue.get_nowait() 相当Queue.get(False)
- Queue.put(item) 写入队列,timeout等待时间
- Queue.put_nowait(item) 相当Queue.put(item, False)
- Queue.task_done() 在完成一项工作之后, Queue.task_done()函数向任务已经完成的队列发送一个信号
- Queue.join() 实际上意味着等到队列为空,再执行别的操作