C# 单个任务与列表<;任务>;在C中#

C# 单个任务与列表<;任务>;在C中#,c#,multithreading,network-programming,C#,Multithreading,Network Programming,我正在用C#创建一个简单的端口扫描器,它应该扫描给定IP范围的开放端口。我在大部分代码中使用了async/await和Task,如下所示: internal class PortScanner { private IPAddress host; private int startPort; private int endPort; private const int PORT_MIN_VALUE = 1; private const int PORT_M

我正在用C#创建一个简单的端口扫描器,它应该扫描给定IP范围的开放端口。我在大部分代码中使用了
async/await
Task
,如下所示:

internal class PortScanner
{
    private IPAddress host;
    private int startPort;
    private int endPort;

    private const int PORT_MIN_VALUE = 1;
    private const int PORT_MAX_VALUE = 65535;

    public PortScanner(IPAddress host, int portStart, int portStop)
    {
        this.host = host;
        this.startPort = portStart;
        this.endPort = portStop;
    }

    public PortScanner(IPAddress host)
        : this(host, PORT_MIN_VALUE, PORT_MIN_VALUE)
    {
    }

    private async Task<bool> IsPortOpen(int port)
    {
        Socket socket = null;

        try
        {
            // make a TCP based socket
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // connect
            await Task.Run(() => socket.Connect(this.host, port));

            return true;
        }
        catch (SocketException ex)
        {
            if (ex.SocketErrorCode == SocketError.ConnectionRefused)
            {
                return false;
            }
        }
        finally
        {
            if (socket?.Connected ?? false)
            {
                socket?.Disconnect(false);
            }
            socket?.Close();
        }

        return false;
    }

    private async Task CheckPort(int port)
    {
        if (await IsPortOpen(port))
        {
            Console.WriteLine("Host: {0} - Port: {1} is open.", this.host.ToString(), port.ToString());
        }
    }

    public async Task Scan()
    {
        Console.WriteLine("Scanning for {0}", this.host);
        for (int port = this.startPort; port <= this.endPort; port++)
        {
            await CheckPort(port);
        }
    }
}
它工作得很好,从某种意义上说,我得到了如下输出(需要几秒钟才能完成):

但是,当我尝试使用
列表时,例如:

foreach (var set in setOfIPs)
{
    List<Task> scanTasks = new List<Task>(set.Count());

    foreach (var ip in set)
    {
        scanTasks.Add(Task.Factory.StartNew(async () =>
        {
            PortScanner ps = new PortScanner(ip, 15, 25);
            await ps.Scan();
        }));
    }

    Task.WaitAll(scanTasks.ToArray());
}

因此,它基本上不会单独扫描每个端口,因为它不会打印任何打开的端口。知道问题可能是什么吗?我如何在
列表中调用多个
任务

这是
任务.Factory.StartNew的经典陷阱。该方法返回一个
Task
,其中
T
是回调的返回类型。lambda是异步的,因此返回一个任务。总而言之,您最终得到一个
任务
,您在等待外部任务,而您应该在等待内部任务

两种解决方案:

  • 建议使用
    Task.Run
    而不是
    Task.Factory.StartNew
    。这是一个一般性建议,除非您知道它是正确的解决方案,否则您永远不应该使用
    Task.Factory.StartNew
    <代码>任务。运行
  • 可避免许多陷阱,例如您遇到的陷阱

    scanTasks.Add(Task.Run(async () =>
    {
        PortScanner ps = new PortScanner(ip, 15, 25);
        await ps.Scan();
    }));
    
  • 如果您有正当理由使用
    Task.Factory.StartNew
    ,则可以对任务使用
    .Unwrap()
    方法来等待内部任务:

    scanTasks.Add(Task.Factory.StartNew(async () =>
    {
        PortScanner ps = new PortScanner(ip, 15, 25);
        await ps.Scan();
    }).Unwrap());
    

  • 您不应该删除并重新发布您的问题:@Servy这可能是因为我将问题缩小到某个方面,并使其更加明确。如果您想澄清您的问题,您应该编辑您的问题,而不是删除并重新发布。@Servy好的,对不起。只是一旦问题被否决,即使你对其进行编辑使其更加明确,也不会引起太多关注。“我想让更多的人关注我的问题”不是删除并重新发布你的问题的有效理由。非常感谢。这解决了问题,现在我明白了问题所在。当
    Task.Factory.StartNew
    是合适的选择时,有什么特别的指导原则吗?@打字错误,长话短说:如果你想做一些你不能用
    Task.Run做的事情。运行
    (所以如果你问自己这个问题,就意味着你不需要
    Task.Factory.StartNew
    )。更具体地说,它允许您设置自定义任务计划程序,以及任务上的一些标志(例如
    LongRunning
    preferfairy
    Scanning for 192.168.1.3
    Scanning for 192.168.1.2
    Scanning for 192.168.1.1
    Scanning for 192.168.1.4
    Scanning for 192.168.1.5
    
    scanTasks.Add(Task.Run(async () =>
    {
        PortScanner ps = new PortScanner(ip, 15, 25);
        await ps.Scan();
    }));
    
    scanTasks.Add(Task.Factory.StartNew(async () =>
    {
        PortScanner ps = new PortScanner(ip, 15, 25);
        await ps.Scan();
    }).Unwrap());