【问题标题】:Send a string message to multiple threads向多个线程发送字符串消息
【发布时间】:2014-06-13 14:07:05
【问题描述】:

我有一个通过套接字接收消息的 IRC 客户端。

通过这个客户端,我创建了几个连接到 twitch 上其他人聊天频道的机器人。 (这些是经过授权的,而不是垃圾邮件机器人!)。

每个机器人都是在一个单独的线程中创建的,该线程采用通道名称和一些其他参数。

我的问题是我的 IRC 套接字只能绑定到一个端口,它处理所有 IRC 消息,每条消息都有一个 #channel 字符串作为第三个字符串,将其定向到特定通道。这些消息可以在每个机器人内部处理,因为每个机器人都知道其频道的名称。

我的问题是;如何将通过套接字接收的字符串发送到多个线程?

import time
import socket
import threading
import string
import sys
import os

class IRCBetBot:
    #irc ref
    irc = None

    def __init__(self,IRCRef,playerName,channelName,currencyName):

        #assign variables
        self.irc = IRCRef
        self.channel = '#' + channelName

        self.irc.send(('JOIN ' + self.channel + '\r\n') .encode("utf8"))

        #create readbuffer to hold strings from IRC
        readbuffer = ""

        # This is the main loop
        while 1:

            ##readbuffer## <- need to send message from IRC to this variable 

            for line in temp:
                line=str.rstrip(line)
                line=str.split(line)

                if (len(line) >= 4) and ("PRIVMSG" == line[1]) and (self.channel == line[2]) and not ("jtv" in line[0]):
                    #call function to handle user message
                if(line[0]=="PING"):
                    self.irc.send(("PONG %s\r\n" % line[0]).encode("utf8"))




def runAsThread(ircref,userName, channelName, currencyPrefix):
    print("Got to runAsThread with : " + str(userName) + " " + str(channelName) + " " + str(currencyPrefix))
    IRCBetBot(ircref,userName,channelName,currencyPrefix)

# Here we create the IRC connection
#IRC connection variables
nick = 'mybot'                  #alter this value with the username used to connect to IRC eg: "username".
password = "oauth:mykey"        #alter this value with the password used to connect to IRC from the username above.
server = 'irc.twitch.tv'
port = 6667

#create IRC socket
irc = socket.socket()

irc.connect((server, port))

#sends variables for connection to twitch chat
irc.send(('PASS ' + password + '\r\n').encode("utf8"))
irc.send(('USER ' + nick + '\r\n').encode("utf8"))
irc.send(('NICK ' + nick + '\r\n').encode("utf8"))

# Array to hold all the new threads 
threads = [] 
# authorised Channels loaded from file in real program
authorisedChannels = [["user1","#channel1","coin1"],["user2","#channel2","coin2"],["user3","#channel3","coin3"]]

for item in authorisedChannels:
    try:
        userName = item[0]
        channelName = item[1]
        currencyPrefix = item [2]
        myTuple = (irc,userName,channelName,currencyPrefix)
        thread = threading.Thread(target=runAsThread,args = myTuple,)
        thread.start()
        threads.append(thread)
        time.sleep(5) # wait to avoid too many connections to IRC at once from same IP
    except Exception as e:
        print("An error occurred while creating threads.")
        print(str(e))

#create readbuffer to hold strings from IRC
readbuffer = ""

# This is the main loop
while 1:
    readbuffer= readbuffer+self.irc.recv(1024).decode("utf-8")
    temp=str.split(readbuffer, "\n")
    readbuffer=temp.pop( )
    #
    #Need to send readbuffer to each IRCBetBot() created in runAsThread that contains a while 1: loop to listen for strings in its __init__() method.
    #   

print ("Waiting...")

for thread in threads:
    thread.join()

print ("Complete.")

我需要以某种方式将主循环中的读取缓冲区放入在单独线程中创建的每个 IRCBetBot 对象中吗?有什么想法吗?

【问题讨论】:

  • 您是想将 readbuffer 发送到每个线程,还是只发送消息实际用于的线程?
  • 这两种方法都可以,我只需要将消息发送到适当的线程,但可以在手头或线程中完成装配。我认为在线程中这样做可能会更好,因为它知道它是谁,即它的频道名称。

标签: python multithreading sockets python-3.x


【解决方案1】:

这是一个示例,说明如何使用每个线程的队列来执行此操作。我们不只是创建一个线程列表,而是创建一个以通道为键的线程字典,并在字典中存储线程对象和可用于与线程对话的队列。

#!/usr/bin/python3

import threading
from queue import Queue


class IRCBetBot(threading.Thread):
    def __init__(self, q, playerName, channelName, currencyName):
        super().__init__()
        self.channel = channelName
        self.playerName = playerName
        self.currencyName = currencyName
        self.queue = q 

    def run(self):
        readbuffer = ""
        while 1:
            readbuffer = self.queue.get()  # This will block until a message is sent to the queue.
            print("{} got msg {}".format(self.channel, readbuffer))

if __name__ == "__main__":

    authorisedChannels = [["user1","#channel1","coin1"],
                          ["user2","#channel2","coin2"],
                          ["user3","#channel3","coin3"]]

threads = {}
for item in authorisedChannels:
    try:
        userName = item[0]
        channelName = item[1]
        currencyPrefix = item [2]
        myTuple = (userName,channelName,currencyPrefix)
        q = Queue() 
        thread = IRCBetBot(q, *myTuple )
        thread.start()
        threads[channelName] = (q, thread)
    except Exception as e:
        print("An error occurred while creating threads.")
        print(str(e))

while 1:
    a = input("Input your message (channel: msg): ")
    channel, msg = a.split(":")
    threads[channel][0].put(msg)  # Sends a message using the queue object

如您所见,当消息进入套接字时,我们会解析出通道(您的代码已经这样做了),然后将消息传递到我们线程字典中的适当队列。

示例输出(稍作调整,因此输出不会因并发 print 调用而被打乱):

dan@dantop:~$ ./test.py 
Input your message (channel: msg): #channel1: hi there
#channel1 got msg  hi there
Input your message (channel: msg): #channel2: another one
#channel2 got msg  another one

【讨论】:

  • 这绝对是我正在努力将其整合到我的代码中的方式。您是否有理由从 IRCBebot 构造函数中删除 IRCRef 引用?我收到了书面的消息,但偶尔 IRCBebot 将消息发送回错误的频道,我将 IRCRef 重新合并到 IRCBetBot 中,这是原因吗?
  • @Zac,我刚刚删除了IRCRef,因为没有必要演示如何在线程之间传递消息。我不确定您看到的问题的原因是什么......当IRCRef 发送消息时,它是否明确说明它应该转到哪个频道?
  • 现在可以正常工作了,如何一次向所有频道发送消息?我尝试将线程放入:for循环:“for item in threads”,但我不知道如何从那里索引队列,并且 item[0].put() 只返回错误:字符串没有 put 方法。
  • @Zac 当你迭代这样的字典时,它只返回键,而不是值。所以你会这样做:for channel in threads: threads[channel].put()
  • 我知道我应该避免谢谢,但谢谢:我用来执行上述操作的代码是“for k, v in threads.items(): v[0].put(line)”
【解决方案2】:

一种方法是使用一个类似于线程数组的 readBuffers 数组。然后每个线程基本上都在等待其特定读取缓冲区上的数据。

当您获取数据时,您可以将其传递给您感兴趣的线程,或者只是将数据复制到所有读取缓冲区,并让线程在他们感兴趣时处理它。在这种情况下,观察者模式效果最好。

【讨论】:

  • 如何确定哪个 readbuffer 被哪个线程读取?您是否建议使用threads[] 和readbuffers[] 并保持每个索引相同,即:treads[0] readbuffers[0]。如果是这样,我如何从我的线程中访问/读取 readbuffer[x​​]?
  • 是的,基于 1-1 索引的映射可以工作。如果 readBuffers 是一个类似于线程的全局变量,那么您可以从线程访问 readBuffers 没问题:)
猜你喜欢
  • 1970-01-01
  • 2018-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
  • 2012-05-24
相关资源
最近更新 更多