简体   繁体   中英

Python global variable is not being accessed in function

This is my python module that is being ran as two threads in main. One thread runs monitor_wind() which adds to wind_count every spin and another thread runs calculate_speed() to get wind in mph from the count, and add it to a list that is later averaged for average wind in mph.

My problem is that calculate_speed() doesn't seem to be accessing the global variable wind_count , it is always 0 even when monitor_wind() is adding to. But the changes it makes to wind_list can be seen in the globals() list.

How can I get it to access the global variable?

from gpiozero import Button
import math
from time import sleep

wind_sensor = Button(6)
wind_count = 0
wind_list = []

def calculate_speed():
    while True:
        global wind_list, wind_count
        print(wind_count)
        radius_cm = 9.0
        cm_in_mile = 160934.4
        circumference_cm = (2 * math.pi) * radius_cm
        rotations = wind_count / 2.0
        dist_miles = (circumference_cm * rotations) / cm_in_mile
        speed = dist_miles / 5 # Divide distance by time, 5 seconds
        print(speed)
        wind_list.append(speed)
        wind_count = 0 # Reset wind count
        sleep(5)

def spin():
    global wind_count
    wind_count += 1

def monitor_wind():
    wind_sensor.when_pressed = spin

To me, this does not appear to be an issue with your use of globals, though I would discourage using globals, and instead make a class WindMonitor . You should also use a lock if you are accessing a variable from multiple threads.

Try this out (I had to call spin on random time intervals because I don't have gpios on my device).

wspeed.py

# from gpiozero import Button
import math
import random
import threading
from time import sleep

DEFAULT_WIND_SENSOR = None; # Button(6)

class WindMonitor:
    def __init__(self, button=DEFAULT_WIND_SENSOR):
        self.wind_count = 0
        self.wind_list = []
        self.button = button
        self.wind_count_lock = threading.Lock()
    
    def calculate_speed(self, stop_event):
        while True:
            with self.wind_count_lock:
                print(self.wind_count)
                radius_cm = 9.0
                cm_in_mile = 160934.4
                circumference_cm = (2 * math.pi) * radius_cm
                rotations = self.wind_count / 2.0
                dist_miles = (circumference_cm * rotations) / cm_in_mile
                speed = dist_miles / 5 # Divide distance by time, 5 seconds
                print(speed)
                self.wind_list.append(speed)
                self.wind_count = 0 # Reset wind count
            if stop_event.wait(5):
                return
    
    def spin(self):
        with self.wind_count_lock:
            self.wind_count += 1

    def monitor_wind(self):
        # self.button.when_pressed = self.spin
        while True:
            sleep(float(random.randint(1, 10)) / 10)
            self.spin()

__main__.py

import threading as th
import time
from wspeed import WindMonitor

if __name__ == '__main__':
    monitor = WindMonitor()
    stop_event = th.Event()
    th_wmonitor = th.Thread(target=monitor.monitor_wind, daemon=True)
    th_wspeed = th.Thread(target=monitor.calculate_speed, args=[stop_event], daemon=True)
    try:
        th_wspeed.start()
        th_wmonitor.start()
        while True: time.sleep(100)
    except (KeyboardInterrupt, SystemExit):
        stop_event.set()

Edit: Explaining stop event

All of your threads loop forever (main thread, th_wmonitor , and th_wspeed ). You want the program to exit when the main terminates.

When python exits, it waits for all non-daemonic threads to terminate, so you did the right thing by making them daemonic. The problem is that python will only exit when there is a single remaining daemonic thread. Because both daemonic threads loop infinitely, you need a way to tell one (or both) of them to when the program exits.

The simplest way of achieving this is to use a threading.Event object. An Event is a simple way of communicating between threads. It's basically a flag that you set in one thread and check in another. By having an exception in main, you can set the stop_event instance of threading.Event . This lets any thread with access to stop_event that the main thread has exited, and other threads may terminate when they are ready to terminate.

The th_wspeed needs to terminate at a specific time because you don't want to terminate in the middle of a print statement. For that reason, I made th_wspeed the one to check stop_event . When th_wspeed sees stop_event has been set, it returns, thus terminating one of the two daemonic threads. At that point, because python sees that only one daemonic thread remains, it will force that thread to terminate, and the program exits.

Without the stop event, the program would not terminate when you, for example, press CTRL+C.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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