在Console中工作但在Windows窗体中不工作的异步代码

时间:2011-10-14 11:29:39

标签: c# winforms multithreading asynchronous

我正在尝试编写一个不断在局域网上搜索主机的应用程序。当我作为控制台运行它作为倒计时.Wait()似乎工作正常。但是,当我将代码带入windows窗体时,countdown.Signal()似乎没有减少它的计数器。不确定是什么问题。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Net;
using System.Threading;

namespace Multi_Threaded
{
    public partial class Form1 : Form
    {

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        PortScanner ps = new PortScanner();
        ps.ProbeCompleted += new PingProbeCompleted(ps_ProbeCompleted);

        ps.run_ping_probe();
    }

    void ps_ProbeCompleted(object sender, PingProbeCompletedArguments e)
    {
        MessageBox.Show("I found " + e.ip_adresses_list_of_host.Count.ToString() + "host(s)");
    }
}

public delegate void PingProbeCompleted(object sender,PingProbeCompletedArguments e);
public class PingProbeCompletedArguments : EventArgs
{
    public List<string> ip_adresses_list_of_host;
}
public class PortScanner
{
    public event PingProbeCompleted ProbeCompleted;
    static List<string> ip_adresses = new List<string>();

    static CountdownEvent countdown;

    public void run_ping_probe()
    {
        ip_adresses.Clear();

        countdown = new CountdownEvent(1);

        string ipBase = "10.125.";
        for (int sub = 0; sub < 14; sub++)
        {
            for (int i = 1; i < 255; i++)
            {
                string ip = ipBase + sub.ToString() + "." + i.ToString();
                Ping p = new Ping();
                p.PingCompleted += new PingCompletedEventHandler(p_PingCompleted);
                countdown.AddCount();
                p.SendAsync(ip, 100, ip);
            }
        }
        countdown.Signal();
        countdown.Wait();
        PingProbeCompletedArguments e = new PingProbeCompletedArguments();
        e.ip_adresses_list_of_host = ip_adresses;
        ProbeCompleted(this, e);

    }

    private void p_PingCompleted(object sender, PingCompletedEventArgs e)
    {
        string ip = (string)e.UserState;
        if (e.Reply.Status == IPStatus.Success)
        {
            ip_adresses.Add(ip + "\t" + e.Reply.RoundtripTime + " ms");
        }
        countdown.Signal();
    }
}

2 个答案:

答案 0 :(得分:6)

是的,当您在Winforms项目中使用它时,代码会死锁。问题是Ping类尽力在调用SendAsync()的同一线程上引发PingCompleted事件。它使用AsyncOperationManager.CreateOperation()方法来执行此操作。

问题是,这实际上适用于Winforms应用程序。它试图在主线程上引发事件。但是,由于您使用countdown.Wait()调用阻止主线程,因此无效。由于主线程被阻止,ping无法完成。由于ping未完成,主线程无法完成。死锁城市。

它在控制台模式应用程序中工作,因为它没有像Winforms那样的同步提供程序。 PingComplete事件将在线程池线程上引发。

阻止UI线程存在根本缺陷。快速解决方法是在工作线程上运行代码。请注意,这也会导致ProbeCompleted事件在该worker上触发。使用Control.BeginInvoke()将其封送到UI线程。或者使用BackgroundWorker。

    private void Form1_Load(object sender, EventArgs e) {
        PortScanner ps = new PortScanner();
        ps.ProbeCompleted += new PingProbeCompleted(ps_ProbeCompleted);
        ThreadPool.QueueUserWorkItem((w) => ps.run_ping_probe());
    }

不要忘记删除额外的Signal()调用。

答案 1 :(得分:0)

你的等待处理程序在线程池的一个线程下运行。 你需要回到UI线程来更新UI(由于运行UI的消息循环) - 为此你使用SynchronizationContext

以下有关如何使用它的更多信息: http://www.codeproject.com/KB/threads/SynchronizationContext.aspx