Как мне управлять проблемами перекрытия чтения / записи TCP-клиента? - PullRequest
1 голос
/ 26 января 2012

У меня есть TCP-клиент, взаимодействующий с LabVIEW GUI.

Моя программа вызывает connect() в начале и disconnect() в конце. Он будет вызывать passCommand(x) для чтения или записи данных в графический интерфейс LabVIEW. Однако в некоторых случаях у меня есть несколько потоков, которые могут вызывать passCommand(), и каким-то образом возвращаемые данные будут перепутаны.

Например, в основном потоке я буду просить напряжение, которое должно быть числом от 300 до 400. В другом потоке я буду просить температуру, которая должна быть числом от 0 до 100. Напряжение будет возвращено как 25, а температура получит 250.

Это известная проблема с TCP-связью и многопоточностью? Есть ли способ решить эту проблему, например, реализовать очередь, уникальный идентификатор или что-то еще?

import socket as _socket

# get python major version as integer
from sys import version as pythonVersion
pythonVersionMajor = int(pythonVersion[0])

_serverHost = 'localhost'
_serverPort = 50007
isConnected = 0
_sockobj = None
_error_string = "error:"


def connect():
    'opens a connection to LabVIEW Server'
    global _sockobj, isConnected
    _sockobj = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)      # create socket
    _sockobj.connect((_serverHost, _serverPort))   # connect to LV
    isConnected = 1


def disconnect():
    'closes the connection to LabVIEW Server'
    global isConnected
    _sockobj.close()                             # close socket
    isConnected = 0


def passCommand(command):
    'passes a command to LabVIEW Server'

    ## We prepend the command length (8 char long) to the message and send it to LV
    # Compute message length and pad with 0 on the left if required
    commandSize=str(len(command)).rjust(8,'0')
    # Prepend msg size to msg
    completeCommand=commandSize+command
    # python 3 requires data to be encoded
    if (pythonVersionMajor >= 3):
        completeCommand = str.encode(completeCommand)
    # Send complete command
    _sockobj.send(completeCommand)
    data = _sockobj.recv(11565536)
    # python 3 requires data to be decoded
    if (pythonVersionMajor >= 3):
        data = bytes.decode(data)
    if data.rfind(_error_string) == 0:
        error = True
        data = data[len(_error_string):] # get data after "error:" string
    else:
        error = False
    execString = "lvdata = " + data
    exec(execString, globals())
    if error:
        raise _LabVIEWError(lvdata)
    else:
        return lvdata


class _Error(Exception):
    """Base class for exceptions in this module."""
    pass


class _LabVIEWError(_Error):
    """Exception raised for errors generated in LabVIEW.

    Attributes:
        code -- LabVIEW Error Code
        source -- location of the error
        message -- explanation of the error
    """

    def __init__(self, error):
        self.code = error[0]
        self.source = error[1]        
        self.message = error[2]

    def __str__(self):
        return "%s" % (self.message,)

1 Ответ

4 голосов
/ 26 января 2012

Это пример одной из самых распространенных проблем с многопоточностью.Вы обращаетесь к ресурсу из нескольких потоков, и этот ресурс не считается потокобезопасным (если оба потока отправляют / получают одновременно, поток может получить неправильный ответ или даже оба ответы).

В идеале вы должны блокировать доступ к passCommand с помощью мьютекса, чтобы он мог использоваться только одним потоком за раз, или открывать один сокет на поток, или выполнять все операции с сокетами водна нить.

...