Python信号量

Python信号量教程

信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。

也可以简单的理解为,信号量是多把锁,同时允许多个线程来更改数据,而 互斥锁 同时只允许一个 线程 更改数据。

Python信号量与互斥锁的关系

信号量的一个特殊用法是互斥量。互斥量是初始值为 1 的信号量,可以实现数据、资源的互斥访问。

Python信号量使用详解

语法

import threading sem = threading.Semaphore(3) sem.acquire() sem.release()

说明

首先,我们需要使用 threading.Semaphore 创建一个信号量的实例,创建实例时,需要指定一个 value 参数 大小,表示内部维护的计数器的大小,默认为 1。

接着,在我们对临界资源进行访问的时候,调用 acquire(),此时内置计数器 -1,直到为 0 的时候就阻塞。资源调用完毕后调用 release(),内置计数器 +1,并让某个线程的 acquire() 从阻塞变为不阻塞。

案例

Python多线程信号量使用

使用 threading.Semaphore 信号量控制多线程

import threading import time def run(n): semaphore.acquire() time.sleep(1) print("thread:%s is running" % n) semaphore.release() if __name__ == '__main__': print("嗨客网(www.haicoder.net)") semaphore = threading.Semaphore(3) #最多允许3个线程同时运行 for i in range(3): t = threading.Thread(target=run, args=(i,)) t.start() while threading.active_count() != 1: pass else: print('----all threads done---')

程序运行后,控制台输出如下:

14 python信号量.png

我们在 main 函数中,首先使用了 threading.Semaphore 创建了一个信号量,同时,传入了参数 3,即最多允许 3 个线程同时并发的执行。

接着,我们使用 for 循环 开启了 3 个线程,是三个线程同时运行,在每个线程处理函数里面,我们都首先使用信号量的实例调用 acquire() 函数,线程运行结束,都调用一次信号量实例的 release() 函数释放资源。

从运行结果来看,我们的线程的确是并发执行的,理论上三个线程完全可以同时运行,最后的 while 循环 的作用相当于 join() 等待线程执行完毕,如果活跃数为1,则说明只有主线程,则表明线程全部运行结束。

Python多线程信号量使用

使用 threading.Semaphore 信号量控制多线程

import threading import time class htmlSpider(threading.Thread): def __init__(self, url, sem): super().__init__() self.url = url self.sem = sem def run(self): time.sleep(2) print("got haicoder text success") self.sem.release() # 内部维护的计数器加1,并通知内部维护的conditon通知acquire class UrlProducer(threading.Thread): def __init__(self, sem): super().__init__() self.sem = sem def run(self): for i in range(20): self.sem.acquire() # 内部维护的计数器减1,到0就会阻塞 html_thread = htmlSpider("http://www.haicoder.net/{}".format(i), self.sem) html_thread.start() if __name__ == "__main__": print("嗨客网(www.haicoder.net)") sem = threading.Semaphore(3) #设置同时最多3个 url_producer = UrlProducer(sem) url_producer.start()

程序运行后,控制台输出如下:

15 python信号量.png

我们在 main 函数中,首先使用了 threading.Semaphore 创建了一个信号量,同时,传入了参数 3,即最多允许 3 个线程同时并发的执行。接着,我们创建了一个 UrlProducer 线程,用来模拟爬取 url,同时在 UrlProducer 线程里,我们使用 for 循环一次性开启了 20 个 htmlSpider 线程,用来模拟爬取 url 对应的网页。

这里,如果我们直接开 20 个 htmlSpider 线程,20 个线程是同时执行的,可能会造成服务器的压力过大,所以我们为了限制一次性最多只允许 3 个线程同时爬取,这里在每开启一个线程 htmlSpider 之前,我们都使用 acquire() 函数获取一个资源,每次 htmlSpider 线程运行结束,我们使用 release() 函数,释放资源。

从运行结果来看,我们的线程的确是并发执行的,理论上三个线程完全可以同时运行,因此,在这里,我们通过信号量实现了模拟控制爬虫的线程数的场景。

Python信号量教程总结

信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。

也可以简单的理解为,信号量是多把锁,同时允许多个线程来更改数据,而互斥锁同时只允许一个线程更改数据。Python信号量使用语法:

import threading sem = threading.Semaphore(3) sem.acquire() sem.release()