📅  最后修改于: 2023-12-03 15:22:28.252000             🧑  作者: Mango
信号量是一种线程同步机制,用于在多个线程之间协调共享资源的访问。它可以用于防止多个线程同时访问共享资源,以避免数据不一致的情况。
信号量是一个计数器,由一个整数表示,可以原子地增加或减少。当一个线程需要访问共享资源时,它会尝试获取信号量,如果信号量的值大于零,线程会将信号量的值减一并继续执行。如果信号量的值为零,线程将被阻塞,直到信号量的值大于零为止。当一个线程使用完共享资源后,它会将信号量的值加一表示释放资源。
在信号量的基础上,还有许多不同的类型,下面我们来介绍一下它们。
二元信号量,也称为互斥锁,只有两个可能的值:0和1。在多个线程中使用互斥锁时,只有一个线程可以访问共享资源,其他线程被阻塞。
在Python中,可以使用threading模块来创建和使用二元信号量。下面是一个例子:
import threading
lock = threading.Lock()
def increment(counter):
with lock:
counter += 1
return counter
counter = 0
threads = []
for i in range(10):
thread = threading.Thread(target=increment, args=(counter,))
threads.append(thread)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print(counter) # 10
上面的代码创建了一个互斥锁,并在多个线程中使用它来保护对计数器变量的递增操作。由于互斥锁只允许一个线程访问共享资源,因此代码输出的结果应该是10。
计数信号量是一个整数值,它可以在多个线程之间共享。当一个线程访问共享资源时,它会将计数信号量的值减一。如果计数信号量的值为零,则线程将被阻塞,直到另一个线程将其加一。当一个线程完成访问共享资源时,它会将计数信号量的值加一。
在Python中,可以使用threading模块来创建和使用计数信号量。下面是一个例子:
import threading
sema = threading.Semaphore(3)
def access_resource(resource_id):
with sema:
# Access to shared resource
pass
threads = []
for i in range(10):
thread = threading.Thread(target=access_resource, args=(i,))
threads.append(thread)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
上面的代码创建了一个计数信号量,初始值为3。然后,它在多个线程中使用计数信号量来控制对共享资源的访问。由于计数信号量的值为3,因此只有三个线程可以同时访问共享资源。其他线程将被阻塞,直到其中一个线程完成访问。
Bounded Semaphore 是一种特殊的计数信号量,它有一个额外的上限值。当计数信号量的值达到上限时,任何尝试获取它的线程都会被阻塞。只有当信号量的值小于上限时,线程才可以成功获取它。
在Python中,可以使用multiprocessing模块来创建和使用Bounded Semaphore。下面是一个例子:
import multiprocessing
sema = multiprocessing.BoundedSemaphore(3)
def access_resource(resource_id):
with sema:
# Access to shared resource
pass
processes = []
for i in range(10):
process = multiprocessing.Process(target=access_resource, args=(i,))
processes.append(process)
for process in processes:
process.start()
for process in processes:
process.join()
上述代码创建了一个Bounded Semaphore,初始值为3。然后,它在多个进程中使用Bounded Semaphore来控制对共享资源的访问。由于Bounded Semaphore的值为3,因此只有三个进程可以同时访问共享资源。其他进程将被阻塞,直到其中一个进程完成访问。
Condition是一种复杂的同步原语,它结合了锁和信号量的功能。线程可以使用Condition来等待另一个线程的通知,并在满足某个条件时执行操作。
在Python中,可以使用threading模块来创建和使用Condition。下面是一个例子:
import threading
cond = threading.Condition()
def worker_thread():
with cond:
cond.wait()
# Do some work
def main_thread():
with cond:
cond.notify()
上述代码创建了一个Condition,并在一个线程中等待通知。在另一个线程中,我们可以使用notify方法通知运行需要执行操作的线程。
Event是一种同步原语,它可用于多个线程之间的通信。一个线程可以等待某个事件的信号,并在触发事件时执行相应的操作。
在Python中,可以使用threading模块来创建和使用Event。下面是一个例子:
import threading
event = threading.Event()
def worker_thread():
event.wait()
# Do some work
def main_thread():
event.set()
上面的代码创建了一个Event,并在一个线程中等待它的信号。在另一个线程中,我们可以使用set方法来触发事件,并允许等待线程执行操作。
Semaphore和Lock都是用于实现线程同步的机制。它们的主要区别在于,Semaphore允许多个线程同时访问共享资源,而Lock只允许一个线程访问共享资源。
当访问共享资源的顺序无关紧要时,应该使用Semaphore。当需要确保一次只有一个线程访问共享资源时,应该使用Lock。
下面是一个例子,展示了Semaphore和Lock的区别:
import threading
sema = threading.Semaphore(3)
lock = threading.Lock()
def access_resource_with_semaphore(resource_id):
with sema:
# Access to shared resource
pass
def access_resource_with_lock(resource_id):
with lock:
# Access to shared resource
pass
threads = []
for i in range(10):
thread = threading.Thread(target=access_resource_with_semaphore, args=(i,))
threads.append(thread)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print('Access with semaphore done.')
threads = []
for i in range(10):
thread = threading.Thread(target=access_resource_with_lock, args=(i,))
threads.append(thread)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print('Access with lock done.')
上述代码展示了如何使用Semaphore和Lock来访问共享资源。在第一个阶段中,我们创建了一个Semaphore并使用它来控制对共享资源的访问。在第二个阶段中,我们创建了一个Lock,并使用它来确保一次只有一个线程访问共享资源。通过观察输出结果,我们可以看到这两种机制的差异。
参考资料: