Py学习  »  Python

Python并行编程(三):多线程同步之semaphore(信号量)实现简易生产者-消费者模型

若数 • 4 年前 • 467 次点击  
阅读 38

Python并行编程(三):多线程同步之semaphore(信号量)实现简易生产者-消费者模型

什么是信号量

semaphore信号量,其控制着对公共资源或者临界区的访问。信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。 semaphore是一个内置的计数器 每当调用acquire()时,内置计数器-1;如果计数器为负数,即资源正在被占用,需要挂起等待 每当调用release()时,内置计数器+1;增加到正数时,队列中的第一个等待线程就可以访问共享资源了

模拟Lock()锁机制

如果我们将计数器设置为1即,第一次线程是不需要等待信号量的释放的,参照上节代码可以进行对比:


import threading
import time
resoure = 0

count = 1000000

semaphore = threading.Semaphore(1)


def increment():
    global resoure
    for i in range(count):
        semaphore.acquire()
        resoure += 1
        semaphore.release()

def decerment():
    global resoure
    for i in range(count):
        semaphore.acquire()
        resoure -= 1
        semaphore.release()


increment_thread = threading.Thread(target=increment)
decerment_thread = threading.Thread(target=decerment)

increment_thread.start()
decerment_thread.start()

increment_thread.join()
decerment_thread.join()

print(resoure)

复制代码

简易生产者-消费者模型


import threading
import random
import time

semaphore = threading.Semaphore(0)

# 假设生产的资源
item_number = 0

# 消费者
def consumer():
    print('Consumer is waiting for Producer')

    # 等待获取信号量
    semaphore.acquire()

    print('get the product , number is {}'.format(item_number))

# 生产者
def producer():
    global item_number

    # 模拟生产资源过程
    time.sleep(2)
    item_number = random.randint(1, 100)
    time.sleep(2)

    print('made the product , number is {}'.format(item_number))

    # 释放信号量
    semaphore.release()

if __name__ == "__main__":
    for i in range(5):

        # 将生产者、消费者实例化为线程
        thread_consumer = threading.Thread(target=consumer)
        thread_producer = threading.Thread(target=producer)

        thread_consumer.start()
        thread_producer.start()

        thread_consumer.join()
        thread_producer.join()

    print('consumer-producer example end.')

复制代码

运行截图如下:

运行结果
我们可以看见两个线程运行时的规律,即消费者必须等待生产者生产好商品(即释放资源),消费者才能获取消费资源(即访问资源),其余时间消费者线程都处于挂起等待(等待信号量)。

利用信号量控制并发数量

利用semaphore我们就可以设置同时访问某些共享资源的线程数量,即通过设设置信号量的值来控制线程同时访问的数量,比如我们可以控制爬虫程序访问链接的线程数量(似乎这样可以实现一定的异步),减少目标网站的压力,同时信号量也支持上下文管理器:


import threading
import random
import time

# 信号量为三即能够释放的资源为三次
semaphore = threading.Semaphore(3)      # 互斥锁+队列   相当于一个容器,容器里同时最大可以存在五个钥匙,同时也只能有五个线程,
                                        # 谁先拿到并释放后,下一个才能拿到钥匙

# 假定url序号
order = 0

def spider():
    global order
    with semaphore:
        # 模拟采集过程
        time.sleep(2)
        order +=1
        
        print('{} is crawlering on {}th url'.format(threading.currentThread().getName(), order))
        time.sleep(2)
         
Threads = []
for i in range(10):
    t = threading.Thread(target=spider)
    Threads.append(t)
    t.start()
    
for t in Threads:
    t.join()

print('Spider end.')

复制代码

运行截图如下:

运行结果

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/31434
 
467 次点击