首页 > 解决方案 > python中的异步,线程问题

问题描述

我有以下代码,其中我有一个无限循环,它更新在一个线程中运行的变量的状态,而在另一个线程中,我有一个无限循环,每秒打印所述变量的状态,问题是虽然状态在第一个线程中正确更新,状态在第二个线程中保持不受影响。

我能做些什么来解决这个问题?我认为使用线程可以解决问题,而且我对异步编程不是很精通

在 main.py 里面

import cv2 as cv
from utils import WindowCapture
import numpy as np
import os
import time
from threading import Thread
from threading import Event
import pyautogui
from queue import Queue


os.chdir(os.path.dirname(os.path.abspath(__file__)))

wincap = WindowCapture("DragonBallOnline")
global toggle
global enemyState

# >>> pyautogui.mouseDown(); pyautogui.mouseUp()  # does the same thing as a left-button mouse click
# >>> pyautogui.mouseDown(button='right')  # press the right button down
# >>> pyautogui.mouseUp(button='right', x=100, y=200)  # move the mouse to 100, 200, then release the right button up.


def targetIsFullHealth(enemy):
    return (enemy == np.array([65, 27, 234])).all()


def targetIsDead(enemy):
    return (enemy == np.array([26, 10, 95])).all()


def targetIsUndefined(enemy):
    return (enemy != np.array([65, 27, 234])).all() and (enemy != np.array([97, 155, 146])).all()


def combatLoop():  # unused
    ## target enemy
    ## if target is full health attack
    ## if target is dead  wait and loot
    ## if target is undefined search new target
    pass


def bot():  # unused
    # Debug function not used currently
    while True:
        ##do stuff
        EnemyLife = wincap.get_screenshot(902, 70, 120, 16)
        Items = wincap.get_screenshot(621, 163, 662, 749)
        Radar = wincap.get_screenshot(1750, 53, 157, 157)
        cv.imshow("EnemyLife", EnemyLife)
        cv.imshow("Items", Items)
        cv.imshow("Radar", Radar)
        # print(EnemyLife[10][5])

        if (EnemyLife[10][5] == np.array([65, 27, 234])).all():
            print("target is full health")
            rotateCursor()
        elif (EnemyLife[10][5] == np.array([97, 155, 146])).all():
            print("Target is dead")
        else:
            print("No target")

        if cv.waitKey(1) == ord("-"):
            cv.destroyAllWindows()
            break

    print("Done!")


def rotateCursor():  # unused
    # pyautogui.mouseDown(button="right")
    # pyautogui.mouseUp(button="right", x=10, y=1)
    pass


def updateEnemyState(threadname):
    global toggle
    global enemyState
    toggle = True

    while True:
        EnemyLife = wincap.get_screenshot(902, 70, 120, 16)
        cv.imshow("EnemyLife", EnemyLife)
        enemy = EnemyLife[10][5]

        if targetIsDead(enemy):
            enemyState = "dead"

        elif targetIsFullHealth(enemy):
            enemyState = "alive"

        else:
            enemyState = "undefined"

        Event().wait(1.0 / 60)
        print("Thread1 :", enemyState)

        if cv.waitKey(1) == ord("-"):
            cv.destroyAllWindows()
            toggle = False
            break


def macros(threadname):

    while toggle:
        Event().wait(1.0)

        print("Thread2 :", enemyState)


if __name__ == "__main__":
    print("test")
    # WindowCapture.list_window_names();

    thread1 = Thread(target=updateEnemyState, args=("Thread-1",))
    thread2 = Thread(target=macros, args=("Thread-1",))
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()


在utils.py里面

import numpy as np
import win32gui, win32ui, win32con


class WindowCapture:

    # properties
    w = 0
    h = 0
    hwnd = None
    cropped_x = 0
    cropped_y = 0
    offset_x = 0
    offset_y = 0

    # constructor
    def __init__(self, window_name=None):
        # find the handle for the window we want to capture
        if window_name is None:
            self.hwnd = win32gui.GetDesktopWindow()
        else:
            self.hwnd = win32gui.FindWindow(None, window_name)
            if not self.hwnd:
                raise Exception('Window not found: {}'.format(window_name))

        # get the window size
        window_rect = win32gui.GetWindowRect(self.hwnd)
        self.w = window_rect[2] - window_rect[0]
        self.h = window_rect[3] - window_rect[1]

        # account for the window border and titlebar and cut them off
        #border_pixels = 8
        #titlebar_pixels = 30
        border_pixels = 0
        titlebar_pixels = 0
        self.w = self.w - (border_pixels * 2)
        self.h = self.h - titlebar_pixels - border_pixels
        self.cropped_x = border_pixels
        self.cropped_y = titlebar_pixels

        # set the cropped coordinates offset so we can translate screenshot
        # images into actual screen positions
        self.offset_x = window_rect[0] + self.cropped_x
        self.offset_y = window_rect[1] + self.cropped_y

    def get_screenshot(self,startX=None,startY=None,width=None,height=None):
        if startX is None : startX = self.cropped_x
        if startY is None : startY = self.cropped_y
        if width is None : width = self.w
        if height is None : height = self.h


        # get the window image data
        wDC = win32gui.GetWindowDC(self.hwnd)
        dcObj = win32ui.CreateDCFromHandle(wDC)
        cDC = dcObj.CreateCompatibleDC()
        dataBitMap = win32ui.CreateBitmap()
        dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
        cDC.SelectObject(dataBitMap)
        cDC.BitBlt((0, 0), (width, height), dcObj, (startX, startY), win32con.SRCCOPY)

        # convert the raw data into a format opencv can read
        #dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
        signedIntsArray = dataBitMap.GetBitmapBits(True)
        img = np.fromstring(signedIntsArray, dtype='uint8')
        img.shape = (height, width, 4)

        # free resources
        dcObj.DeleteDC()
        cDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, wDC)
        win32gui.DeleteObject(dataBitMap.GetHandle())

        img = img[...,:3]
        img = np.ascontiguousarray(img)

        return img

    @staticmethod
    def list_window_names():
        def winEnumHandler(hwnd, ctx):
            if win32gui.IsWindowVisible(hwnd):
                print(hex(hwnd), win32gui.GetWindowText(hwnd))
        win32gui.EnumWindows(winEnumHandler, None)

    # translate a pixel position on a screenshot image to a pixel position on the screen.
    # pos = (x, y)
    # WARNING: if you move the window being captured after execution is started, this will
    # return incorrect coordinates because the window position is only calculated in
    # the __init__ constructor.
    
    def get_screen_position(self, pos):
        return (pos[0] + self.offset_x, pos[1] + self.offset_y)

我已经提供了重新创建示例所需的所有代码,尽管最重要的部分是硬编码的,因此它是一个像素检测器,您可能需要使用另一个窗口名称和另一个像素位置和颜色值

初始和第一个状态变化:

Thread1 : alive
Thread1 : alive
Thread1 : alive
Thread1 : alive
Thread1 : alive
Thread2 : alive
Thread1 : alive
Thread1 : alive
Thread1 : alive
Thread1 : alive
Thread1 : alive

调试信息

Thread1 : dead
Thread1 : dead
Thread1 : dead
Thread1 : dead
Thread2 : alive
Thread1 : dead
Thread1 : dead
Thread1 : dead
Thread1 : dead
Thread1 : dead
Thread1 : dead

Thread2 的enemyState 应该已经死了 我的猜测是,由于某种原因,enemyState 仍然被锁定在 thread2 中。

标签: pythonmultithreadingasynchronous

解决方案


global语句必须在更改 的值的每个函数中toogle。你不能只包含一次。将该语句移入updateEnemyState,它应该开始工作。该macros函数不需要那个,因为它不会改变值。你可以修正拼写;它应该是toggle


推荐阅读