赞
赏
信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。
也可以简单的理解为,信号量是多把锁,同时允许多个线程来更改数据,而 互斥锁 同时只允许一个 线程 更改数据。
信号量的一个特殊用法是互斥量。互斥量是初始值为 1 的信号量,可以实现数据、资源的互斥访问。
import threading
sem = threading.Semaphore(3)
sem.acquire()
sem.release()
首先,我们需要使用 threading.Semaphore
创建一个信号量的实例,创建实例时,需要指定一个 value 参数 大小,表示内部维护的计数器的大小,默认为 1。
接着,在我们对临界资源进行访问的时候,调用 acquire(),此时内置计数器 -1,直到为 0 的时候就阻塞。资源调用完毕后调用 release(),内置计数器 +1,并让某个线程的 acquire() 从阻塞变为不阻塞。
使用 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---')
程序运行后,控制台输出如下:
我们在 main 函数中,首先使用了 threading.Semaphore 创建了一个信号量,同时,传入了参数 3,即最多允许 3 个线程同时并发的执行。
接着,我们使用 for 循环 开启了 3 个线程,是三个线程同时运行,在每个线程处理函数里面,我们都首先使用信号量的实例调用 acquire() 函数,线程运行结束,都调用一次信号量实例的 release() 函数释放资源。
从运行结果来看,我们的线程的确是并发执行的,理论上三个线程完全可以同时运行,最后的 while 循环 的作用相当于 join() 等待线程执行完毕,如果活跃数为1,则说明只有主线程,则表明线程全部运行结束。
使用 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()
程序运行后,控制台输出如下:
我们在 main 函数中,首先使用了 threading.Semaphore 创建了一个信号量,同时,传入了参数 3,即最多允许 3 个线程同时并发的执行。接着,我们创建了一个 UrlProducer 线程,用来模拟爬取 url,同时在 UrlProducer 线程里,我们使用 for 循环一次性开启了 20 个 htmlSpider 线程,用来模拟爬取 url 对应的网页。
这里,如果我们直接开 20 个 htmlSpider 线程,20 个线程是同时执行的,可能会造成服务器的压力过大,所以我们为了限制一次性最多只允许 3 个线程同时爬取,这里在每开启一个线程 htmlSpider 之前,我们都使用 acquire() 函数获取一个资源,每次 htmlSpider 线程运行结束,我们使用 release() 函数,释放资源。
从运行结果来看,我们的线程的确是并发执行的,理论上三个线程完全可以同时运行,因此,在这里,我们通过信号量实现了模拟控制爬虫的线程数的场景。
信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。
也可以简单的理解为,信号量是多把锁,同时允许多个线程来更改数据,而互斥锁同时只允许一个线程更改数据。Python信号量使用语法:
import threading
sem = threading.Semaphore(3)
sem.acquire()
sem.release()