Python parser=argparse.ArgumentParser() parser.add_参数(“--host”,type=str,help=“绑定到主机”) add_参数(“--port”,type=int,help=“绑定到端口”) add_参数(“--packet size”,type=int,help=“packets的大小”) args=parser.parse_args() 主机,端口=args.HOST,args.PORT 类MyTCPHandler(socketserver.BaseRequestHandler): def句柄(自身): 时间。睡眠(1.5) data=self.request.recv(参数包大小) self.request.sendall(data.upper()) 使用socketserver.ThreadingCpserver((主机,端口),MyTCPHandler)作为服务器: 服务器。永远为您服务()

Python parser=argparse.ArgumentParser() parser.add_参数(“--host”,type=str,help=“绑定到主机”) add_参数(“--port”,type=int,help=“绑定到端口”) add_参数(“--packet size”,type=int,help=“packets的大小”) args=parser.parse_args() 主机,端口=args.HOST,args.PORT 类MyTCPHandler(socketserver.BaseRequestHandler): def句柄(自身): 时间。睡眠(1.5) data=self.request.recv(参数包大小) self.request.sendall(data.upper()) 使用socketserver.ThreadingCpserver((主机,端口),MyTCPHandler)作为服务器: 服务器。永远为您服务(),python,multithreading,performance,multiprocessing,gil,Python,Multithreading,Performance,Multiprocessing,Gil,client.py: import argparse import logging import multiprocessing as mp import os import queue as q import socket import time from threading import Thread def get_logger(): logger = logging.getLogger("threading_example") logger.set

client.py:

import argparse
import logging
import multiprocessing as mp
import os
import queue as q
import socket
import time
from threading import Thread


def get_logger():
    logger = logging.getLogger("threading_example")
    logger.setLevel(logging.INFO)

    fh = logging.FileHandler("client.log")
    fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(fmt)
    fh.setFormatter(formatter)

    logger.addHandler(fh)
    return logger


class PacketMaker(mp.Process):
    def __init__(self, result_queue, max_packets, packet_size, num_poison_pills, logger):
        mp.Process.__init__(self)
        self.result_queue = result_queue
        self.max_packets = max_packets
        self.packet_size = packet_size
        self.num_poison_pills = num_poison_pills
        self.num_packets_made = 0
        self.logger = logger

    def run(self):
        while True:
            if self.num_packets_made >= self.max_packets:
                for _ in range(self.num_poison_pills):
                    self.result_queue.put(None, timeout=1)
                self.logger.debug('PacketMaker exiting')
                return
            self.result_queue.put(os.urandom(self.packet_size), timeout=1)
            self.num_packets_made += 1


class PacketSender(Thread):
    def __init__(self, task_queue, result_queue, addr, packet_size, logger):
        Thread.__init__(self)
        self.task_queue = task_queue
        self.result_queue = result_queue
        self.server_addr = addr
        self.packet_size = packet_size
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect(addr)
        self.logger = logger

    def run(self):
        while True:
            packet = self.task_queue.get(timeout=1)
            if packet is None:
                self.logger.debug("PacketSender exiting")
                return
            try:
                self.sock.sendall(packet)
                response = self.sock.recv(self.packet_size)
            except socket.error:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.connect(self.server_addr)
                self.sock.sendall(packet)
                response = self.sock.recv(self.packet_size)
            self.result_queue.put(response)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--num-packets', type=int, help='number of packets to send')
    parser.add_argument('--packet-size', type=int, help='packet size in bytes')
    parser.add_argument('--num-threads', type=int, help='number of threads sending packets')
    parser.add_argument('--host', type=str, help='name of host packets will be sent to')
    parser.add_argument('--port', type=int, help='port number of host packets will be sent to')
    args = parser.parse_args()

    logger = get_logger()
    logger.info(f"starting script with args {args}")
    
    packets_to_send = mp.Queue(args.num_packets + args.num_threads)
    packets_received = q.Queue(args.num_packets)
    producers = [PacketMaker(packets_to_send, args.num_packets, args.packet_size, args.num_threads, logger)]
    senders = [PacketSender(packets_to_send, packets_received, (args.host, args.port), args.packet_size, logger)
               for _ in range(args.num_threads)]
    start_time = time.time()
    logger.info("starting workers")
    for worker in senders + producers:
        worker.start()
    for worker in senders:
        worker.join()
    logger.info("workers finished")
    end_time = time.time()
    print(f"{packets_received.qsize()} packets received in {end_time - start_time} seconds")
run.sh:

#!/usr/bin/env bash

for i in "$@"
do
case $i in
    -s=*|--packet-size=*)
    packet_size="${i#*=}"
    shift 
    ;;
    -n=*|--num-packets=*)
    num_packets="${i#*=}"
    shift 
    ;;
    -t=*|--num-threads=*)
    num_threads="${i#*=}"
    shift 
    ;;
    -h=*|--host=*)
    host="${i#*=}"
    shift 
    ;;
    -p=*|--port=*)
    port="${i#*=}"
    shift 
    ;;
    *)
    ;;
esac
done

python3 server.py --host="${host}" \
                  --port="${port}" \
                  --packet-size="${packet_size}" &
server_pid=$!
python3 client.py --packet-size="${packet_size}" \
                  --num-packets="${num_packets}" \
                  --num-threads="${num_threads}" \
                  --host="${host}" \
                  --port="${port}"
kill "${server_pid}"
$./run.sh-s=1024-n=1500-t=300-h=localhost-p=9999

在4.70330023765564秒内接收1500个数据包

$./run.sh-s=1024-n=1500-t=1500-h=localhost-p=9999

在1.5025699138641357秒内接收1500个数据包

可以通过将client.py中的日志级别更改为
DEBUG
来验证此结果。请注意,脚本的完成时间确实比4.7秒长得多。当使用300个线程时,需要进行大量的拆卸,但是日志清楚地表明,线程是在4.7秒的时间内完成处理的

对所有的性能结果都持保留态度。我不知道你在什么系统上运行这个。我将提供我的相关系统统计信息: 2 Xeon X5550@2.67GHz 24MB DDR3@1333MHz Debian 10 Python 3.7.3


我将通过您的尝试解决这些问题:

  • 简单单线程:由于
    randint(0,3)
    延迟,这几乎保证至少需要1.5 x num_数据包秒
  • 多线程:GIL可能是这里的瓶颈,但这可能是因为
    craft packet
    部分,而不是
    send packet
  • 多处理:每个进程至少需要文件描述符,因此您可能超过了用户或系统限制,但如果您
  • 多处理+多线程:失败的原因与#2相同,制作数据包可能受到CPU限制

  • 经验法则是:I/O绑定-使用线程,CPU绑定-使用进程

    您是否尝试过将模块用于多路复用套接字?然后你甚至不需要线程。Re:“然而,我能达到的最“并发数据包发送”是~800”,然后发生了什么?没有确凿的证据,我认为选项2(多线程)没有任何实际问题。它甚至可能是最快的。但这完全取决于大部分时间实际花在哪里。你试过gevent吗?我遇到了一个类似的问题,当时我正在模拟一个电话提供商的许多并行电话呼叫。与任何其他设置相比,ZeroMQ+gevent给了我惊人的性能。看看这对你是否有意义。我想在你的情况下,单是gevent就足够了。@l'l'l所以它不起作用,因为我需要“同时”发送1500个数据包。
    from gevent import monkey; monkey.patch_all()
    
    jobs = [gevent.spawn(send_pkt, x) for x in list[:1500]]
    gevent.wait(jobs)
    #for send_pkt(x) check [1]
    
    import concurrent.futures
    import urllib.request
    import time
    
    IPs= ['168.212. 226.204',
            '168.212. 226.204',
            '168.212. 226.204',
            '168.212. 226.204',
            '168.212. 226.204']
    
    def send_pkt(x):
      status = 'Failed'
      while True:
        #send pkt
        time.sleep(10)
        status = 'Successful'
        break
      return status
    
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        future_to_ip = {executor.submit(send_pkt, ip): ip for ip in IPs}
        for future in concurrent.futures.as_completed(future_to_ip):
            ip = future_to_ip[future]
            try:
                data = future.result()
            except Exception as exc:
                print('%r generated an exception: %s' % (ip, exc))
            else:
                print('%r send %s' % (url, data))
    
    def send_pkts(ip):
    #craft packet
    while True:
        #send packet
        time.sleep(randint(0,3))
    
    import argparse
    import logging
    import multiprocessing as mp
    import os
    import queue as q
    import socket
    import time
    from threading import Thread
    
    
    def get_logger():
        logger = logging.getLogger("threading_example")
        logger.setLevel(logging.INFO)
    
        fh = logging.FileHandler("client.log")
        fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
        formatter = logging.Formatter(fmt)
        fh.setFormatter(formatter)
    
        logger.addHandler(fh)
        return logger
    
    
    class PacketMaker(mp.Process):
        def __init__(self, result_queue, max_packets, packet_size, num_poison_pills, logger):
            mp.Process.__init__(self)
            self.result_queue = result_queue
            self.max_packets = max_packets
            self.packet_size = packet_size
            self.num_poison_pills = num_poison_pills
            self.num_packets_made = 0
            self.logger = logger
    
        def run(self):
            while True:
                if self.num_packets_made >= self.max_packets:
                    for _ in range(self.num_poison_pills):
                        self.result_queue.put(None, timeout=1)
                    self.logger.debug('PacketMaker exiting')
                    return
                self.result_queue.put(os.urandom(self.packet_size), timeout=1)
                self.num_packets_made += 1
    
    
    class PacketSender(Thread):
        def __init__(self, task_queue, result_queue, addr, packet_size, logger):
            Thread.__init__(self)
            self.task_queue = task_queue
            self.result_queue = result_queue
            self.server_addr = addr
            self.packet_size = packet_size
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect(addr)
            self.logger = logger
    
        def run(self):
            while True:
                packet = self.task_queue.get(timeout=1)
                if packet is None:
                    self.logger.debug("PacketSender exiting")
                    return
                try:
                    self.sock.sendall(packet)
                    response = self.sock.recv(self.packet_size)
                except socket.error:
                    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    self.sock.connect(self.server_addr)
                    self.sock.sendall(packet)
                    response = self.sock.recv(self.packet_size)
                self.result_queue.put(response)
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        parser.add_argument('--num-packets', type=int, help='number of packets to send')
        parser.add_argument('--packet-size', type=int, help='packet size in bytes')
        parser.add_argument('--num-threads', type=int, help='number of threads sending packets')
        parser.add_argument('--host', type=str, help='name of host packets will be sent to')
        parser.add_argument('--port', type=int, help='port number of host packets will be sent to')
        args = parser.parse_args()
    
        logger = get_logger()
        logger.info(f"starting script with args {args}")
        
        packets_to_send = mp.Queue(args.num_packets + args.num_threads)
        packets_received = q.Queue(args.num_packets)
        producers = [PacketMaker(packets_to_send, args.num_packets, args.packet_size, args.num_threads, logger)]
        senders = [PacketSender(packets_to_send, packets_received, (args.host, args.port), args.packet_size, logger)
                   for _ in range(args.num_threads)]
        start_time = time.time()
        logger.info("starting workers")
        for worker in senders + producers:
            worker.start()
        for worker in senders:
            worker.join()
        logger.info("workers finished")
        end_time = time.time()
        print(f"{packets_received.qsize()} packets received in {end_time - start_time} seconds")
    
    #!/usr/bin/env bash
    
    for i in "$@"
    do
    case $i in
        -s=*|--packet-size=*)
        packet_size="${i#*=}"
        shift 
        ;;
        -n=*|--num-packets=*)
        num_packets="${i#*=}"
        shift 
        ;;
        -t=*|--num-threads=*)
        num_threads="${i#*=}"
        shift 
        ;;
        -h=*|--host=*)
        host="${i#*=}"
        shift 
        ;;
        -p=*|--port=*)
        port="${i#*=}"
        shift 
        ;;
        *)
        ;;
    esac
    done
    
    python3 server.py --host="${host}" \
                      --port="${port}" \
                      --packet-size="${packet_size}" &
    server_pid=$!
    python3 client.py --packet-size="${packet_size}" \
                      --num-packets="${num_packets}" \
                      --num-threads="${num_threads}" \
                      --host="${host}" \
                      --port="${port}"
    kill "${server_pid}"