在多线程程序中添加 time.sleep 解决了 python 中的 UnicodeDecodeError

Posted

技术标签:

【中文标题】在多线程程序中添加 time.sleep 解决了 python 中的 UnicodeDecodeError【英文标题】:Adding a time.sleep to a multithreaded program solves a UnicodeDecodeError in python 【发布时间】:2016-06-08 03:21:53 【问题描述】:

这是我在程序中创建的线程的基本概念:

 Main thread
        |
   ListenerCreator(The WebSocketServer thread)  ---> Several listener threads(using log())

所以主线程创建了一个ListenerCreator线程,它连接到多个客户端,并为每个客户端创建一个监听线程。以下是侦听器线程所做的简要说明: 编辑1: 我正在使用 WebSockets 从客户端读取/写入数据。为此,我制作了自己的服务器。有一个标准指定的framing protocol——我正在使用它。在客户端,我只是使用WebSocket.send() 并根据协议中给出的说明“取消屏蔽”消息(请参阅上面链接中的第 5.3 节)。 如果有人要求,我愿意提供服务器代码,但是,这里有一个简短的大纲:

class WebSocketServer:
    def start(): 
          #Open server socket, bind to host:port
          while True:
              #Accept client socket, start a new listener thread for self.log(client)
    def log(client):
          #Receive data using socket.socket.recv(1024)
          #Unmask data as per the protocol
          #Decode using data.decode("utf-8")
          #Append to data_q while holding data_q_lock

还有其他方法 - 方便发送、关闭、握手等。

同时在主线程中:

   while breaking!=len(client_list):
        #time.sleep(0.5)    
        with data_q_lock:
           for i in range(len(data_q)):
                mes = data_q.pop()
                for m in client_list:
                    if "#DONE"== mes:
                        breaking += 1
                if(mes[:len("#COUNT:")] == "#COUNT:"):
                    print(mes)

所以基本上这个循环的作用是:遍历data_q,如果消息以“#COUNT”开头,则打印该消息,并在获得一定数量的“#DONE”消息后退出循环。 如果未注释 time.sleep,则此代码有效,但是如果没有 time.sleep,我会在 log 函数中得到 UnicodeDecodeError。 另外我只收到错误 sometimes ,有时程序运行良好。 (顺便说一下,客户端每次都发送相同的数据) 所以,我的问题是,为什么需要 time.sleep ? 我认为这与 python 中的 GIL 有关,因为 time.sleep 发布了 GIL。但是,即使在阅读后我也无法解决问题

【问题讨论】:

请展示您如何从侦听器中的套接字读取数据,这与您的问题非常相关:-) 【参考方案1】:

目前没有关于监听器如何从套接字读取数据的信息。然而,这似乎是由于通常对套接字的误解造成的。

通过套接字发送的数据不会被套接字以任何方式“框定”。想象一下,如果我在一个套接字上发送了 3 次“hello”消息。然后,就像在没有换行符的情况下写入文件一样,以下内容将在套接字上流动:

hellohellohello

现在考虑阅读器......在阅读数据时,它如何知道一条消息(“hello”)从哪里开始以及下一条消息从哪里开始?它不能,除非发送者和接收者就应该如何“构建”数据达成一致。这可以通过同意一些协议来完成,例如:

空终止数据;或 固定大小的消息;或 大小前缀消息。

当然,它会变得更复杂,即使您已经决定了数据应该如何构建,您也不能保证socket.recv 会返回一个“完整”消息......它只会返回恰好包含的任何数据当时的缓冲区。它可能是半条消息,也可能是半条消息。您的工作是整理从套接字读取的数据并将其划分为消息。

转向您的问题,您正在发送utf-8 数据。读者如何知道它已阅读完整的utf-8 数据消息?最有可能的是,这里发生的事情是您只收到了 部分消息 ...还有更多消息要到达。

特别是,有效的utf-8 字符可能包含多个字节。因此,如果您的部分消息在字符的多字节 utf-8 表示的中间结束,那么您肯定无法对其进行解码。

【讨论】:

谢谢!我添加了更多关于我实际使用套接字的方式的信息。我根据 WebSocket 协议使用大小前缀消息。 您收到UnicodeDecodeError 几乎肯定表明您的数据有效负载不完整...尝试捕获此错误并检查实际帧以确保它是您的想法。 (我假设您意识到 socket.recv(1024) 可能会返回少于 1024 个字节?)

以上是关于在多线程程序中添加 time.sleep 解决了 python 中的 UnicodeDecodeError的主要内容,如果未能解决你的问题,请参考以下文章

uWSGI, Thread, time.sleep 使用问题

time.sleep -- 休眠线程或进程?

python time.sleep()-睡眠线程还是进程?

python中from time import sleep是啥意思

线程-GIL锁

线程-GIL锁