线程只运行一次函数,并且只运行一次 returns 值?如何连续return函数取值?

Thread runs function only once, and only returns value once? How to continuously return function value?

我有一个函数可以从红外热像仪读取数据,处理数据并 returns 一个值。在下面的代码中,returns 是检测到的最低温度。这个函数的计算量很大,所以我想 运行 它在一个单独的线程中。

在下面的示例中,我有一个启动线程的 class。这一次有效。它读取传感器和 returns 温度。但它再也没有 运行 的功能。它保持 returning 相同的温度值,即使我改变了传感器的输入。该函数在单独的程序中独立运行并不断更新温度。

我希望函数在线程中 运行 因为我也在播放声音和控制 LED。

如何在线程中多次获取 运行 函数,以便我可以连续或定期获取主线程中的临时值?

我尝试使用线程 class,但我一定是做错了什么。我也尝试过使用队列,但从未获得任何数据 return。

import queue
import sys
import pygame
import cv2
import random
import math
import colorsys
import time
from rpi_ws281x import *
from PIL import Image
import numpy as np
import threading

sys.path.insert(0, "/home/pi/irpython/build/lib.linux-armv7l-3.5")
import MLX90640 as mlx

# IR Function

def irCounter():
  while True:  
        img = Image.new( 'L', (24,32), "black") # make IR image

        mlx.setup(8) #set frame rate of MLX90640

        f = mlx.get_frame()

        mlx.cleanup()     

        for x in range(24):
            row = []
            for y in range(32):
                val = f[32 * (23-x) + y]
                row.append(val)
                img.putpixel((x, y), (int(val)))

        # convert raw temp data to numpy array
        imgIR = np.array(img)

        ## Threshold the -40C to 300 C temps to a more human range
        # Sensor seems to read a bit cold, calibrate in final setting
        rangeMin = 6 # low threshold temp in C
        rangeMax = 20 # high threshold temp in C

        # Apply thresholds based on min and max ranges
        depth_scale_factor = 255.0 / (rangeMax-rangeMin)
        depth_scale_beta_factor = -rangeMin*255.0/(rangeMax-rangeMin)

        depth_uint8 = imgIR*depth_scale_factor+depth_scale_beta_factor
        depth_uint8[depth_uint8>255] = 255
        depth_uint8[depth_uint8<0] = 0
        depth_uint8 = depth_uint8.astype('uint8')

        # increase the 24x32 px image to 240x320px for ease of seeing
        bigIR = cv2.resize(depth_uint8, dsize=(240,320), interpolation=cv2.INTER_CUBIC)

        # Normalize the image
        normIR = cv2.normalize(bigIR, bigIR, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)

        # Use a bilateral filter to blur while hopefully retaining edges
        brightBlurIR = cv2.bilateralFilter(normIR,9,150,150)

        # Threshold the image to black and white 
        retval, threshIR = cv2.threshold(brightBlurIR, 210, 255, cv2.THRESH_BINARY)

        # Define kernal for erosion and dilation and closing operations
        kernel = np.ones((5,5),np.uint8)

        erosionIR = cv2.erode(threshIR,kernel,iterations = 1)

        dilationIR = cv2.dilate(erosionIR,kernel,iterations = 1)

        closingIR = cv2.morphologyEx(dilationIR, cv2.MORPH_CLOSE, kernel)

        # Detect countours
        contours, hierarchy = cv2.findContours(closingIR, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

        # Get the number of contours ( contours count when touching edge of image while blobs don't)
        #ncontours = str(len(contours))
        ncontours = max(f)

        # Show images in window during testing
        #cv2.imshow("Combined", closingIR)

        return ncontours
        cv2.waitKey(1)

#initialize pygame
pygame.init()
pygame.mixer.init()
pygame.mixer.set_num_channels(30)
print("pygame initialized")

# assign sound chennels for pygame
channel0 = pygame.mixer.Channel(0)
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)


# load soundfiles
echoballs = pygame.mixer.Sound("echo balls bounce.ogg")
organbounce = pygame.mixer.Sound("ORGAN BOUNCE.ogg")
jar = pygame.mixer.Sound("jar.ogg")
garland = pygame.mixer.Sound("GARLAND.ogg")
dribble= pygame.mixer.Sound("dribble.ogg")

# initializing sounds list  
soundsList = [echoballs, organbounce, jar, garland, dribble]
# use random.sample() to shuffle sounds list 
shuffledSounds = random.sample(soundsList, len(soundsList))

IRcount = 0 # for testing only

pygame.display.set_mode((32, 8)) # need display for keyboard input

# LED strip configuration:
LED_COUNT      = 256      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10     # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 100     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53

# Define functions which animate LEDs in various ways.
plasmaTime = 0.0 # time
plasmaSpeed = 0.5 # speed of time

def sineLED1 ():
    h = 8
    w = 32
    out = [ Color( 0, 0, 0 ) for x in range( h * w ) ]
    plasmaBright = 100.0
    for x in range( h ):
        for y in range( w ):
            hue = ((128+(128*math.sin(y + plasmaTime/ 8))))/256
            hsv = colorsys.hsv_to_rgb(.5, 1,hue )
            if y % 2 == 0: #even
                out[ x + (h * y)] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
            else: #odd
                out[ (y * h) + (h -1 -x) ] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
    for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
        strip.setPixelColor(i, out[ i ]) # set pixel to color in picture
    strip.show()


# Threading class to get temp from IR function
class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):
        self.ir_temp = irCounter()

    def start(self):
        self.thread.start()

# Main program logic follows:
if __name__ == '__main__':

    # start thread
    task = TempTask()
    task.start()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

    print ('Press Ctrl-C to quit.')

    try:
        while True:
            #simulate increase / decreat of people count from IRsensor for testing until irCounter function non-blocking
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        IRcount -= 1
                        print(IRcount)
                    if event.key == pygame.K_RIGHT:
                        IRcount += 1
                        print(IRcount)
                break
            if IRcount == 0:
                print(task.ir_temp) # print temp from sensor, only prints first time function runs
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)     
            elif IRcount == 1:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(1).get_busy() == False: channel1.play(shuffledSounds[1],loops = -1)
            elif IRcount == 2:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(2).get_busy() == False: channel2.play(shuffledSounds[2],loops = -1)
            elif IRcount == 3:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(3).get_busy() == False: channel3.play(shuffledSounds[3],loops = -1)
            elif IRcount == 4:
                sineLED1()
                if pygame.mixer.Channel(4).get_busy() == False: channel4.play(shuffledSounds[4],loops = -1)

    except KeyboardInterrupt:
       colorWipe(strip, Color(0,0,0), 1)
       pygame.mixer.stop()

我已经搜索了论坛并尝试了很多东西,但不知道接下来要尝试什么。

这是上面完整代码的片段,我在其中创建了线程 class 并启动了线程

class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):
        self.ir_temp = irCounter()

    def start(self):
        self.thread.start()

# Main program logic follows:
if __name__ == '__main__':

    # start thread
    task = TempTask()
    task.start()

非常感谢任何有关下一步尝试的帮助或建议。提前谢谢你。

要使其持续更新,您需要修改 TempTask class 使其 update_temp() 方法包含一个循环,并向其中添加一个 Lock控制对 ir_temp 属性的并发访问,以允许多个线程安全地访问它。

请注意,尚不清楚是否真的需要 Lock,因为除了在主线程中读取 TempTask 实例的属性外,您什么都不做 — 但 [=13= 中的循环] 方法需要保持 运行 irCounter() 功能。您可能还想更改 while True: 以引用另一个(附加)实例属性,该属性控制是否保留 运行。

补充说明:

不清楚为什么在 irCounter() 中有 while True: 循环,因为它在接近尾部时有 return 阻止它重复不止一次。这样做并不重要,但我建议您将其删除。

class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.lock = threading.Lock()  # ADDED
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):  # MODIFIED
        while True:
            with self.lock:
                self.ir_temp = irCounter()
            time.sleep(0.1)  # Polling delay.

    def start(self):
        self.thread.start()

除此之外,您还需要更改在主循环中读取共享属性的位置(参见 # ADDED 行):

     .
     .
     .
    try:
        while True:  # This while loop doesn't iterate - suggest removal.
            #simulate increase / decrease of people count from IRsensor for testing until irCounter function non-blocking
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        IRcount -= 1
                        print(IRcount)
                    if event.key == pygame.K_RIGHT:
                        IRcount += 1
                        print(IRcount)
                break
            if IRcount == 0:
                with task.lock:  # ADDED.
                    print(task.ir_temp) # print temp from sensor
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)
            elif IRcount == 1:
                sineLED1()
                   .
                   .
                   .