多线程爬虫
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。 最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。
import threading
import time
def coding():
for x in range(3):
print("写代码%s"%threading.current_thread())#threading.current_thread()可以写出线程名字,eg:<Thread(Thread-1, started 20112)>
time.sleep(1)
def writing():
for x in range(3):
print("写东西%s"%threading.current_thread())
time.sleep(1)
def main():
t1=threading.Thread(target=coding)
t2 = threading.Thread(target=writing)
t1.start()
t2.start()
print(threading.enumerate())#可以查看有多少线程,此程序有3个,其中包括一个主线程
if __name__=='__main__':
main()
可以写成类的形式
import threading
import time
class coding_thread(threading.Thread):
def run(self):
for x in range(3):
print("写代码%s"%threading.current_thread())#threading.current_thread()可以写出线程名字,eg:<Thread(Thread-1, started 20112)>
time.sleep(1)
class writing_thread(threading.Thread):
def run(self):
for x in range(3):
print("写东西%s"%threading.current_thread())
time.sleep(1)
def main():
t1=coding_thread()
t2 =writing_thread()
t1.start()
t2.start()
print(threading.enumerate())#可以查看有多少线程,此程序有3个,其中包括一个主线程
if __name__=='__main__':
main()
多线程共享全局变量的问题:
多线程都是在同一个进程中运行的,进程中的全局变量所有线程都可以共享的。为了解决这个问题,可以采用加锁的方式,当一个进程在执行的时候,全局变量被锁住,另一个线程不得改变全局变量,当前线程写完后,锁释放,另一个线程进行写操作。但是如果仅仅是访问而不改变变量的话,就没有必要采用锁机制。
import threading
VALUE=1
gLock=threading.Lock()
def add_value():
global VALUE
gLock.acquire()
for i in range(1000000):
VALUE +=1
gLock.release()
print(VALUE)
def main():
for i in range(2):
t=threading.Thread(target=add_value)
t.start()
if __name__=='__main__':
main()
而没有加锁之前是这样的:
生产者消费者模式
Lock版本生产者和消费者模式:
生产者的线程专门用来产生一些数据,然后存放在一个中间变量里面。消费者再从这个中间变量中去除数据进行消费。这个模式需要使用锁模式来保证数据的完整性。
如下的例子是定义了两个全局变量,一个是钱,一个是生产者生产钱的次数,使用while True来保证一直生产和消费,但是当生产者生产钱的次数到达了10次之后,就会退出循环不要忘记释放锁,如果消费者判断出来钱不够了并且消费者不产钱了,也会退出循环。
import threading
import random
import time
gMoney=1000
gLock=threading.Lock()
gTimes=0
Times=10
class Costumer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money=random.randint(100,1000)
gLock.acquire()
if gTimes>10:
gLock.release()
break
gMoney+=money
gTimes+=1
gLock.release()
print("%s生产了%d钱,剩余%d钱"%(threading.current_thread(),money,gMoney))
time.sleep(0.5)
class Producer(threading.Thread):
def run(self):
while True:
global gMoney
money=random.randint(100,1000)
gLock.acquire()
if money<=gMoney:
gMoney-=money
print("%s消费了%d钱,剩余%d钱"%(threading.current_thread(),money,gMoney))
else:
if gTimes>10:
gLock.release()
break
print("钱不够了")
gLock.release()
time.sleep(0.5)
def main():
for i in range(5):
t=Costumer(name="生产者%d"%i)
t.start()
for i in range(5):
t=Producer(name="消费者%d"%i)
t.start()
if __name__=="__main__":
main()
Condition版的生产者和消费者模式:
Lock版本的生产者消费者模式总是通过while True死循环上锁的方式判断钱够不够。上锁是一个很耗费CPU的资源的行为,还有一种方式是使用threading.Condition来实现。threading.Condition可以在没有数据的时候处于阻塞等待状态,一旦有了合适的数据之后,使用notify相关的函数通知处于等待状态的线程,这样可以不用做无谓的上锁和释放锁的行为。
wait:当前的线程处于等待状态,并且会释放锁。可以被其他线程用notify相关函数唤醒,并继续等待,直到上锁。
notify:通知某个等待的线程,默认是第一个等待的线程
notify_all:通知所有等待的线程。notify并不会释放锁,所以要在release之前使用
import threading
import random
import time
gMoney=1000
condition=threading.Condition()
gTimes=0
Times=10
class Costumer(threading.Thread):
def run(self):
global gMoney
global gTimes
while True:
money=random.randint(100,1000)
condition.acquire()
if gTimes>10:
condition.release()
break
gMoney+=money
gTimes+=1
condition.notify_all()
condition.release()
print("%s生产了%d钱,剩余%d钱"%(threading.current_thread(),money,gMoney))
time.sleep(0.5)
class Producer(threading.Thread):
def run(self):
while True:
global gMoney
money=random.randint(100,1000)
condition.acquire()
while money>gMoney:
if gTimes>10:
condition.release()
return
print("%s准备消费%d钱,剩余%d钱" % (threading.current_thread(), money, gMoney))
# gLock.acquire()
condition.wait()
gMoney -= money
print("%s消费了%d钱,剩余%d钱"%(threading.current_thread(),money,gMoney))
# gLock.acquire()
# if money<=gMoney:
# gMoney-=money
# print("%s消费了%d钱,剩余%d钱"%(threading.current_thread(),money,gMoney))
# else:
# if gTimes>10:
# gLock.release()
# break
# print("钱不够了")
condition.release()
time.sleep(0.5)
def main():
for i in range(5):
t=Costumer(name="生产者%d"%i)
t.start()
for i in range(5):
t=Producer(name="消费者%d"%i)
t.start()
if __name__=="__main__":
main()