Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
353 views
in Technique[技术] by (71.8m points)

c# - Asynchronous code that works in Console but not in Windows Forms

I am trying to write an application the is constantly searching for host on a lan. When I run this as a console as the countdown.Wait() seems to work fine. However when I bring the code into a windows form the countdown.Signal() does not seem to decrement its counter. Not sure what the problem is.

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 + "" + e.Reply.RoundtripTime + " ms");
        }
        countdown.Signal();
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Yes, your code deadlocks when you use it in a Winforms project. The problem is that the Ping class makes a best effort to raise the PingCompleted event on the same thread that called SendAsync(). It uses the AsyncOperationManager.CreateOperation() method to do so.

Problem is, that actually works in a Winforms app. It tries to raise the event on the main thread. But that cannot work since you blocked the main thread with the countdown.Wait() call. The ping cannot complete since the main thread is blocked. The main thread cannot complete since the ping doesn't complete. Deadlock city.

It works in a Console mode app since it doesn't have a synchronization provider like Winforms does. The PingComplete event will be raised on a threadpool thread.

Blocking the UI thread is fundamentally flawed. The quick fix is to run the code on a worker thread. Beware that this makes the ProbeCompleted event fired on that worker as well. Use Control.BeginInvoke() to marshal it to the UI thread. Or use 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());
    }

And don't forget to remove the extra Signal() call.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...