【问题标题】:Single Task vs List<Task> in C#C# 中的单任务与列表<Task>
【发布时间】:2017-12-20 21:24:56
【问题描述】:

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

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);
        }
    }
}

现在,重点是,如果在 Main 函数中,我使用单个 Task,如下所示:

try
{
    PortScanner ps = new PortScanner(ipsInRange.Begin, 15, 25);
    var task = ps.Scan();
    task.Wait();
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

它工作正常,从某种意义上说,我得到了一个输出,例如(并且需要几秒钟才能完成):

Scanning for 192.168.1.1
Host: 192.168.1.1 - Port: 21 is open.
Host: 192.168.1.1 - Port: 23 is open.

但是,当我尝试使用List&lt;Task&gt; 时,例如:

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());
}

我得到的输出就是这个(程序也在 1 秒内立即完成):

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

因此,它基本上似乎不会单独扫描它们中的每一个,因为它不会打印任何打开的端口。任何想法可能是什么问题,以及如何在List&lt;Task&gt; 中调用多个Task

【问题讨论】:

  • 您不应该删除并重新发布您的问题:stackoverflow.com/questions/47912486/…
  • @Servy 这可能是因为我将问题缩小到某个问题,并使其更加明确。
  • 如果你想澄清你的问题,你应该编辑你的问题,而不是删除它并重新发布。
  • @Servy 好的,抱歉。只是一旦问题被否决,即使您对其进行编辑以使其更明确,也不会引起太多关注。
  • “我想要更多地关注我的问题”不是删除并重新发布您的问题的正当理由。

标签: c# multithreading network-programming


【解决方案1】:

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

两种解决方案:

  1. 推荐的一种:使用Task.Run 而不是Task.Factory.StartNew。这是一般性建议,除非您知道这是正确的解决方案,否则您永远不应该使用 Task.Factory.StartNewTask.Run 避免了很多陷阱,比如你遇到的那个。

    scanTasks.Add(Task.Run(async () =>
    {
        PortScanner ps = new PortScanner(ip, 15, 25);
        await ps.Scan();
    }));
    
  2. 如果您有正当理由使用Task.Factory.StartNew,那么您可以在任务上使用.Unwrap() 方法以便能够在内部等待:

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

【讨论】:

  • 非常感谢。这解决了问题,现在我明白了问题所在。当Task.Factory.StartNew 是合适的选择时,是否有任何特定的指导方针?
  • @typos 好吧,长话短说:如果你想做一些你不能用Task.Run 做的事情(所以如果你问自己这个问题,这意味着你不需要@987654335 @)。更具体地说,它允许您设置自定义任务调度程序,以及任务上的一些标志(例如LongRunningPreferFairness
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
相关资源
最近更新 更多