如何在 Python 中以交错方式执行线程
How to execute threads in an interleaved way in Python
我几乎是 Python 线程方面的新手。
这是我的代码,以 GeeksForFeeks 上的示例代码为模型,该代码解释了使用锁的线程行为。
但结果 - 对我来说 - 是违反直觉的。
import threading
# global variable x
x = 0
# creating a lock
lock = threading.Lock()
def increment():
global x
x += 1
print("thread1:", x)
def decrement():
global x
x -= 1
print("thread2:", x)
def plus():
global lock
for _ in range(100000):
lock.acquire()
increment()
lock.release()
def minus():
global lock
for _ in range(100000):
lock.acquire()
decrement()
lock.release()
def main_task():
global x
# setting global variable x as 0
x = 0
# creating threads
t1 = threading.Thread(target=plus)
t2 = threading.Thread(target=minus)
# start threads
t1.start()
t2.start()
# wait until threads finish their job
t1.join()
t2.join()
if __name__ == "__main__":
main_task()
我希望得到这样的结果:
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
...
但我得到了
thread1: 1
thread1: 2
thread1: 3
thread1: 4
thread1: 5
thread1: 6
thread1: 7
...
thread1: 151
thread2: 150
thread2: 149
thread2: 148
thread2: 147
thread2: 146
thread2: 145
thread2: 144
thread2: 143
thread2: 142
thread2: 141
...
为什么thread2每次从thread1释放时都无法获取到锁?
我错过了什么?
发生这种情况至少有两个原因:
GIL
是解释器本身的单个锁,它添加了一条规则,即执行任何 Python 字节码都需要获取解释器锁。
更笼统:Lock
对象不太适合您。是的,它同步了对变量的访问,但是在 lock
释放之后和获取它之前,其他代码可以执行多次这样的迭代。
对于交错同步,最好使用Condition
对象,它允许代码等待其他代码通知(本质上是唤醒它)第一个可以执行:
# creating a lock
lock = threading.Condition()
# ...
def plus():
global lock
with lock:
for _ in range(100000):
increment()
lock.notify()
lock.wait()
lock.notify() # notify to finish companion thread
def minus():
global lock
with lock:
for _ in range(100000):
decrement()
lock.notify()
lock.wait()
lock.notify() # notify to finish companion thread
...
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
thread2: 0
我几乎是 Python 线程方面的新手。 这是我的代码,以 GeeksForFeeks 上的示例代码为模型,该代码解释了使用锁的线程行为。 但结果 - 对我来说 - 是违反直觉的。
import threading
# global variable x
x = 0
# creating a lock
lock = threading.Lock()
def increment():
global x
x += 1
print("thread1:", x)
def decrement():
global x
x -= 1
print("thread2:", x)
def plus():
global lock
for _ in range(100000):
lock.acquire()
increment()
lock.release()
def minus():
global lock
for _ in range(100000):
lock.acquire()
decrement()
lock.release()
def main_task():
global x
# setting global variable x as 0
x = 0
# creating threads
t1 = threading.Thread(target=plus)
t2 = threading.Thread(target=minus)
# start threads
t1.start()
t2.start()
# wait until threads finish their job
t1.join()
t2.join()
if __name__ == "__main__":
main_task()
我希望得到这样的结果:
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
...
但我得到了
thread1: 1
thread1: 2
thread1: 3
thread1: 4
thread1: 5
thread1: 6
thread1: 7
...
thread1: 151
thread2: 150
thread2: 149
thread2: 148
thread2: 147
thread2: 146
thread2: 145
thread2: 144
thread2: 143
thread2: 142
thread2: 141
...
为什么thread2每次从thread1释放时都无法获取到锁?
我错过了什么?
发生这种情况至少有两个原因:
GIL
是解释器本身的单个锁,它添加了一条规则,即执行任何 Python 字节码都需要获取解释器锁。更笼统:
Lock
对象不太适合您。是的,它同步了对变量的访问,但是在lock
释放之后和获取它之前,其他代码可以执行多次这样的迭代。
对于交错同步,最好使用Condition
对象,它允许代码等待其他代码通知(本质上是唤醒它)第一个可以执行:
# creating a lock
lock = threading.Condition()
# ...
def plus():
global lock
with lock:
for _ in range(100000):
increment()
lock.notify()
lock.wait()
lock.notify() # notify to finish companion thread
def minus():
global lock
with lock:
for _ in range(100000):
decrement()
lock.notify()
lock.wait()
lock.notify() # notify to finish companion thread
...
thread1: 1
thread2: 0
thread1: 1
thread2: 0
thread1: 1
thread2: 0