简体   繁体   English

如何使线程与 python 中的共享全局变量同步?

[英]How to synchronize threads with shared global variable in python?

i'm currently trying to unterstand threading in python and i wrote a program that ideally would have 2 threads alternating between incrementing and decrementing a global variable but no matter how i spread out the lock it inevitably becomes out of sync.我目前正在尝试理解 python 中的线程,并且我编写了一个程序,理想情况下,该程序将有 2 个线程在全局变量的递增和递减之间交替,但无论我如何分散锁,它都不可避免地变得不同步。

number = 0
lock = threading.Lock()
def func1():
    global number
    global lock
    while True:
        try:
            lock.acquire()
            number += 1
        finally:
            lock.release()
        print(f"number 1 is: {number}")
        time.sleep(0.1)

def func2():
    global number
    global lock
    while True:
        try:
            lock.acquire()
            number -= 1
        finally:
            lock.release()
        print(f"number 2 is: {number}")
        time.sleep(0.1)

t1 = threading.Thread(target=func1)
t1.start()

t2 = threading.Thread(target=func2)
t2.start()

t1.join()
t2.join()

the output should look something like this: output 应如下所示:

number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0

but right now it looks like this:但现在它看起来像这样:

number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 2 is: -1number 1 is: 0

number 2 is: -1number 1 is: 0

number 1 is: 1number 2 is: 0

any idea how to do this without falling out of sync?知道如何在不同步的情况下做到这一点吗?

First, avoid using global variables with threads in python.首先,避免在 python 中对线程使用全局变量。 Use a queue to share the variables instead.改为使用队列来共享变量。

Second, the lock acquisition in non-deterministic.第二,非确定性的锁获取。 At the moment a lock is released, you have no guarantee that the other thread will grab it.在释放锁的那一刻,您无法保证其他线程会抓住它。 There is always a certain probability that the thread that just released the lock can grab it again before the other thread.总是有一定的概率,刚刚释放锁的线程可以在另一个线程之前再次抢到它。

But in your case, you can avoid problems because you know the state that the variable needs to be to accept modifications by one thread or the other.但是在您的情况下,您可以避免问题,因为您知道 state 变量需要接受一个线程或另一个线程的修改。 So, you can enforce the protection for modification by verifying if the variable is in the right state to accept a modification.因此,您可以通过验证变量是否在正确的 state 中来接受修改来强制修改保护。

Something like:就像是:

from threading import Thread
import time
from queue import Queue

def func1(threadname, q):
    while True:
        number = q.get()
        
        if number == 0:
            number += 1
            print(f"number 1 is: {number}")

        q.put(number)
        time.sleep(0.1)

def func2(threadname, q):
    while True:
        number = q.get()

        if number == 1:
            number -= 1
            print(f"number 2 is: {number}")

        q.put(number)
        time.sleep(0.1)

queue = Queue()
queue.put(0)
t1 = Thread(target=func1, args=("Thread-1", queue))
t2 = Thread(target=func2, args=("Thread-2", queue))

t1.start()
t2.start()
t1.join()
t2.join()

For starters, time.sleep is not exactly accurate .对于初学者来说, time.sleep 并不完全准确 And depending on the python-implementation you're using (most likely cpython) multithreading might not quite work the way you're expecting it to .并且根据您使用的 python 实现(很可能是 cpython),多线程可能无法按照您期望的方式工作 These two factors allow the initially correct timing of your threads to get out of sync within fairly short time.这两个因素允许您的线程最初正确的时间在相当短的时间内失去同步。

There solution for this problem is to enforce alternate operation on the variable by the two threads via two locks:这个问题的解决方案是通过两个锁强制两个线程对变量执行交替操作:

import time
import threading

var = 0


def runner(op, waitfor, release):
    global var

    while True:
        try:
            # wait for resource to free up
            waitfor.acquire()

            # operation
            var = op(var)
            print(f"var={var}")
        finally:
            # notify other thread
            release.release()

        time.sleep(0.1)


# init locks for thread-synchronization
lock_a = threading.Lock()
lock_b = threading.Lock()
lock_a.acquire()
lock_b.acquire()

# create and start threads (they'll wait for their lock to be freed)
thread_a = threading.Thread(target=runner, args=(lambda v: v - 1, lock_a, lock_b))
thread_b = threading.Thread(target=runner, args=(lambda v: v + 1, lock_b, lock_a))
thread_a.start()
thread_b.start()

# let thread_b start the first operation by releasing the lock
lock_b.release()

In the above code, each thread has a lock that can be used to notify it, that the resource may be used by it.在上面的代码中,每个线程都有一个锁,可以用来通知它,资源可能被它使用。 Thus threads can hand control over the global variable to each other.因此,线程可以将全局变量的控制权交给彼此。

thanks for all your answers, i remember seing someone in the comments mentioned using events or something about event based threading and that solved my issue.感谢您的所有回答,我记得在使用事件或有关基于事件的线程的评论中看到有人,这解决了我的问题。 here's the code:这是代码:

number = 0
event_number = threading.Event()
event_number.clear()

def func1():
    global number
    global event_number
    while True:
        if not event_number.is_set():
            number += 1
            print(f"func 1 is {number}")
            event_number.set()
        else:
            pass
        time.sleep(2)

def func2():
    global number
    global event_number
    while True:
        if event_number.is_set():
            number -= 1
            print(f"func 2 is {number}")
            event_number.clear()
        else:
            pass
        time.sleep(2)

t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)

t1.start()
t2.start()

t1.join()
t2.join()

now i notice that sometimes one of the loops will either not wait it's alloted time and print right away or wait double the time but at least the number only stays within those 2 values.现在我注意到有时其中一个循环要么不等待分配的时间并立即打印,要么等待两倍的时间,但至少该数字仅保持在这两个值内。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM