爬虫(12)——多线程爬虫(消费者生产者模式、condition模式)

多线程爬虫

多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。 最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。

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()
发布了35 篇原创文章 · 获赞 4 · 访问量 2338

猜你喜欢

转载自blog.csdn.net/devilangel2/article/details/105462103
今日推荐