zoukankan      html  css  js  c++  java
  • 多线程共享全局变量的问题以及生产者与消费者模式

    多线程共享全局变量的问题:

    多线程就是在同个进程中运行的。因此在进程中的全局变量所有线程都可共享。这就造成了一个问题,因为线程执行过程的顺序是无序的,导致有可能造成数据错误:

    这时候就需要加上一把锁,把先进到该进程上锁,即不会让别的线程进入,防止乱序,导致数据出错。特别是当数据特别大时,就容易出错。

    示例代码:

    import threading
    import time
    VALUE = 0
    gLock = threading.Lock()
    def add_value():
        global VALUE
        gLock.acquire()
        for x in range(1000000):
            VALUE += 1
        gLock.release()
        print('value=%d'%VALUE)
        # time.sleep(3)
    
    def main():
        for x in range(2):
            # add_value()
            t = threading.Thread(target=add_value)
            t.start()
    
    
    if __name__ == '__main__':
        main()

    其中gLock就是由threading.Lock()创建的对象,作用就是锁对象。

    gLock.acquire()就是上锁操作,即不会让别的线程进入。

    gLock.release()就是解锁操作,即释放锁。因为该线程操作完成了,可以放其他线程进来,这时就需要释放锁的操作。

    生产者与消费者模式:

    生产者与消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门用来生产一些数据,如何存放到一个中间变量中。

    消费者再从这个中间变量取出数据进行消费。但是因为要使用中间变量,中间变量经常是一些全局变量。

    所以需要上锁机制,防止数据错乱。

    示例代码:

    import threading
    import random
    import time
    
    gMoney = 1000
    gLock = threading.Lock()
    gTotalTimes = 10
    gTimes = 0
    
    class Producer(threading.Thread):
        def run(self):
            global gMoney
            global gTimes
            while True:
                money = random.randint(100,1000)
                gLock.acquire()
                if gTimes >= gTotalTimes:
                    gLock.release()
                    break
                gMoney += money
                print('%s生产了%d元钱,剩余%d元钱'%(threading.current_thread(),money,gMoney))
                gTimes += 1
                gLock.release()
                time.sleep(0.5)
    
    class Consumer(threading.Thread):
        def run(self):
            global gMoney
            while True:
                money = random.randint(100,1000)
                gLock.acquire()
                if gMoney >= money:
                    gMoney -=money
                    print('%s消费了%d元钱,剩余%d元钱'%(threading.current_thread(),money,gMoney))
                else:
                    if gTimes >= gTotalTimes:
                        gLock.release()
                        break
                    print('%s准备消费%d元钱,剩余%d元钱,钱不足!!'%(threading.current_thread(),money,gMoney))
                gLock.release()
                time.sleep(0.5)
    
    def main():
        for x in range(3):
            t = Consumer(name='消费者线程%d'%x)
            t.start()
        for x in range(5):
            t = Producer(name='生产者线程%d'%x)
            t.start()
    
    if __name__ == '__main__':
        main()

    这里采用创建类,并且继承自Thread类。

    当用到全局变量就上锁,用完就释放锁。即用到全局变量都会有 gLock.acquire() ...... gLock.release() 操作。

    threading.current_thread()方法可以得出该线程的名称。

    总之,生产者与消费者模式是一种爬虫思想。示例化就是,生产者可以负责“生产”(爬取)目标的具体 url 列表,消费者是把存放在生产者的“产品”(具体url)给“消费“(爬取需求内容)掉。

    生产者与消费者模式,明显表现了多线程的思想,所以用多线程的时候都可以尝试生产者与消费者模式,可以大大提高爬虫的效率。

  • 相关阅读:
    深入理解MyBatis中的一级缓存与二级缓存
    Spring-mvc文件的上传和下载
    Spring-mvc的拦截器和异常通知
    各种配置文件
    设计模式---代理模式
    dom4j读取xml和dtd的使用方式
    几种不同的路径
    常用正则表达式
    请求转发和重定向的对比
    跨浏览器检测某个节点是不是另一个节点的后代
  • 原文地址:https://www.cnblogs.com/zyde-2893/p/11484463.html
Copyright © 2011-2022 走看看