Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
执行python脚本时,脚本在线程上阻塞,但不是在交互模式下_Python_Multithreading - Fatal编程技术网

执行python脚本时,脚本在线程上阻塞,但不是在交互模式下

执行python脚本时,脚本在线程上阻塞,但不是在交互模式下,python,multithreading,Python,Multithreading,TL;DR:为什么线程化进程从交互式模式(如myprocess.start())按预期运行(分离的python线程),但从shell运行时会阻塞子线程,如python myprocess.py Background:我的类的子类是threading.Thread,它还调用了另外两个Thread-类型的子类。它看起来像: class Node(threading.Thread): def __init__(self, gps_device): threading.Thre

TL;DR:为什么线程化进程从交互式模式(如
myprocess.start()
)按预期运行(分离的python线程),但从shell运行时会阻塞子线程,如
python myprocess.py


Background:我的类的子类是
threading.Thread
,它还调用了另外两个
Thread
-类型的子类。它看起来像:

class Node(threading.Thread):
    def __init__(self, gps_device):
        threading.Thread.__init__(self)
        self.daemon = False

        logging.info("Setting up GPS service")
        self.gps_svc = gps.CoordinateService(gps_device)
        self.gps_svc.daemon = True

        logging.info("Setting up BLE scanning service")
        # TODO: This is blocking when run in terminal (aka how we do on Raspberry Pi)
        self.scan_svc = scan.BleMonitor()
        self.scan_svc.daemon = True

        logging.info("Node initialized - ready for start")

    def run(self):
        self.gps_svc.start()
        self.scan_svc.start()  # blocks here in terminal

        do stuff...
这两项服务(
gps_-svc
scan_-svc
)都可以在交互模式下从解释器按预期工作,如
node=node(…);node.start()
。当我用脚本调用解释器时,
gps\u svc
启动并运行,但是
scan\u svc
在它监听蓝牙设备的特定线路上阻塞

BLE扫描器在下面(很长)。这是
BleMonitor
的父类-没有什么不同,我只是添加了几个实用函数


问题:为什么会发生这种情况?我可以运行/与进程交互还是与线程交互(即:调用类的方法并实时获取数据)

类监视器(threading.Thread):
“”“继续扫描可删除的播发。”“”
def uuu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
“”“构造接口对象。”“”
#在此处进行导入,以便包可以在仅解析模式下使用(不需要bluez)
self.bluez=import\u模块('bluetooth.\u bluetooth'))
threading.Thread.\uuuuu init\uuuuuu(自)
self.daemon=False
self.keep_go=True
self.callback=回调
#bt设备的编号(hciX)
self.bt\u device\u id=bt\u device\u id
#要监视的信标列表
self.device\u filter=设备\u filter
self.mode=获取模式(设备过滤器)
#要监视的数据包类型列表
self.packet\u filter=packet\u filter
#蓝牙插座
self.socket=None
#跟踪Eddystone信标bt地址映射
self.eddystone_映射=[]
def运行(自):
“”“继续扫描可删除的播发。”“”
self.socket=self.bluez.hci\u open\u dev(self.bt\u device\u id)
filter=self.bluez.hci\u filter\u new()
self.bluez.hci_filter_all_事件(filter)
self.bluez.hci_filter_set_ptype(filter,self.bluez.hci_EVENT_PKT)
self.socket.setsockopt(self.bluez.SOL_HCI、self.bluez.HCI_过滤器、过滤器)
自我切换扫描(真)
当self.keep_运行时:
pkt=self.socket.recv(255)
事件=to_int(pkt[1])
子事件=to_int(pkt[3])
如果事件==LE_META_事件和子事件==EVT_LE_广告报告:
#我们有一个很好的广告
自处理_数据包(pkt)
def切换_扫描(自身,启用):
“”“启用和禁用可恢复扫描。”“”
如果启用:
command=“\x01\x00”
其他:
command=“\x00\x00”
self.bluez.hci_send_cmd(self.socket、OGF_LE_CTL、OCF_LE_SET_SCAN_ENABLE、command)
def过程_数据包(自身,包装):
“”“分析数据包并在其中一个筛选器匹配时调用回调。”“”
#在解析之前,请检查此数据包是否有效
#这大大降低了CPU负载
if(self.mode==mode_和\
(pkt[19:21]!=b“\xaa\xfe”)和(pkt[19:23]!=b“\x4c\x00\x02\x15”))\
或者(self.mode==mode_EDDYSTONE和(pkt[19:21]!=b“\xaa\xfe”))\
或者(self.mode==mode_IBEACON和(pkt[19:23]!=b“\x4c\x00\x02\x15”):
返回
bt_addr=bt_addr_to_字符串(pkt[7:13])
rssi=bin_to_int(pkt[-1])
#剥离蓝牙地址并解析数据包
packet=parse_数据包(pkt[14:-1])
#如果数据包不是信标广告,则返回
如果不是数据包:
返回
#我们需要记住哪个eddystone信标有哪个bt地址
#因为TLM和URL框架不包含名称空间和实例
self.save_bt_addr(数据包,bt_addr)
#属性保存信标的标识信息
#例如,eddystone的实例和名称空间;iBeacon的uuid、大调、小调
properties=self.get\u属性(数据包、bt\u地址)
如果self.device_filter为None且self.packet_filter为None:
#未选择任何筛选器
自回调(bt_addr、rssi、数据包、属性)
elif self.device_过滤器为无:
#按包类型筛选
如果是(数据包、自身数据包过滤器)中的一个:
自回调(bt_addr、rssi、数据包、属性)
其他:
#按设备和数据包类型筛选
如果self.packet\u filter和not是(packet,self.packet\u filter)的其中一个:
#如果数据包筛选器不匹配,则返回
返回
#迭代过滤器并在每个过滤器上调用.matches()
对于self.device\u过滤器中的过滤器:
如果存在(过滤器、BtAddrFilter):
如果filter.matches({'bt_addr':bt_addr}):
自回调(bt_addr、rssi、数据包、属性)
返回
elif filter.matches(属性):
自回调(bt_addr、rssi、数据包、属性)
返回
def保存地址(自身、数据包、bt地址):
“”“添加到映射列表。”“”
如果isinstance(数据包、EddystoneUIDFrame):
#删除旧映射
new_映射=[m表示self中的m.eddystone_映射,如果m[0]!=bt_addr]
新的映射.append((bt_addr,packet.properties))
self.eddystone\u映射=新的\u映射
def get_属性(自身、数据包、bt_地址):
“”“根据类型获取信标的属性。”“”
如果是(数据包,[eddystonelmframe,EddystoneURLFrame\
class Monitor(threading.Thread):
    """Continously scan for BLE advertisements."""

    def __init__(self, callback, bt_device_id, device_filter, packet_filter):
        """Construct interface object."""
        # do import here so that the package can be used in parsing-only mode (no bluez required)
        self.bluez = import_module('bluetooth._bluetooth')

        threading.Thread.__init__(self)
        self.daemon = False
        self.keep_going = True
        self.callback = callback

        # number of the bt device (hciX)
        self.bt_device_id = bt_device_id
        # list of beacons to monitor
        self.device_filter = device_filter
        self.mode = get_mode(device_filter)
        # list of packet types to monitor
        self.packet_filter = packet_filter
        # bluetooth socket
        self.socket = None
        # keep track of Eddystone Beacon <-> bt addr mapping
        self.eddystone_mappings = []

    def run(self):
        """Continously scan for BLE advertisements."""
        self.socket = self.bluez.hci_open_dev(self.bt_device_id)

        filtr = self.bluez.hci_filter_new()
        self.bluez.hci_filter_all_events(filtr)
        self.bluez.hci_filter_set_ptype(filtr, self.bluez.HCI_EVENT_PKT)
        self.socket.setsockopt(self.bluez.SOL_HCI, self.bluez.HCI_FILTER, filtr)

        self.toggle_scan(True)

        while self.keep_going:
            pkt = self.socket.recv(255)
            event = to_int(pkt[1])
            subevent = to_int(pkt[3])
            if event == LE_META_EVENT and subevent == EVT_LE_ADVERTISING_REPORT:
                # we have an BLE advertisement
                self.process_packet(pkt)

    def toggle_scan(self, enable):
        """Enable and disable BLE scanning."""
        if enable:
            command = "\x01\x00"
        else:
            command = "\x00\x00"
        self.bluez.hci_send_cmd(self.socket, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, command)

    def process_packet(self, pkt):
        """Parse the packet and call callback if one of the filters matches."""
        # check if this could be a valid packet before parsing
        # this reduces the CPU load significantly
        if (self.mode == MODE_BOTH and \
                (pkt[19:21] != b"\xaa\xfe") and (pkt[19:23] != b"\x4c\x00\x02\x15")) \
                or (self.mode == MODE_EDDYSTONE and (pkt[19:21] != b"\xaa\xfe")) \
                or (self.mode == MODE_IBEACON and (pkt[19:23] != b"\x4c\x00\x02\x15")):
            return

        bt_addr = bt_addr_to_string(pkt[7:13])
        rssi = bin_to_int(pkt[-1])
        # strip bluetooth address and parse packet
        packet = parse_packet(pkt[14:-1])

        # return if packet was not an beacon advertisement
        if not packet:
            return


        # we need to remember which eddystone beacon has which bt address
        # because the TLM and URL frames do not contain the namespace and instance
        self.save_bt_addr(packet, bt_addr)
        # properties hold the identifying information for a beacon
        # e.g. instance and namespace for eddystone; uuid, major, minor for iBeacon
        properties = self.get_properties(packet, bt_addr)

        if self.device_filter is None and self.packet_filter is None:
            # no filters selected
            self.callback(bt_addr, rssi, packet, properties)

        elif self.device_filter is None:
            # filter by packet type
            if is_one_of(packet, self.packet_filter):
                self.callback(bt_addr, rssi, packet, properties)
        else:
            # filter by device and packet type
            if self.packet_filter and not is_one_of(packet, self.packet_filter):
                # return if packet filter does not match
                return

            # iterate over filters and call .matches() on each
            for filtr in self.device_filter:
                if isinstance(filtr, BtAddrFilter):
                    if filtr.matches({'bt_addr':bt_addr}):
                        self.callback(bt_addr, rssi, packet, properties)
                        return

                elif filtr.matches(properties):
                    self.callback(bt_addr, rssi, packet, properties)
                    return

    def save_bt_addr(self, packet, bt_addr):
        """Add to the list of mappings."""
        if isinstance(packet, EddystoneUIDFrame):
            # remove out old mapping
            new_mappings = [m for m in self.eddystone_mappings if m[0] != bt_addr]
            new_mappings.append((bt_addr, packet.properties))
            self.eddystone_mappings = new_mappings

    def get_properties(self, packet, bt_addr):
        """Get properties of beacon depending on type."""
        if is_one_of(packet, [EddystoneTLMFrame, EddystoneURLFrame, \
                              EddystoneEncryptedTLMFrame, EddystoneEIDFrame]):
            # here we retrieve the namespace and instance which corresponds to the
            # eddystone beacon with this bt address
            return self.properties_from_mapping(bt_addr)
        else:
            return packet.properties

    def properties_from_mapping(self, bt_addr):
        """Retrieve properties (namespace, instance) for the specified bt address."""
        for addr, properties in self.eddystone_mappings:
            if addr == bt_addr:
                return properties
        return None

    def terminate(self):
        """Signal runner to stop and join thread."""
        self.toggle_scan(False)
        self.keep_going = False
        self.join()