Асинхронный код, который работает в консоли, но не в Windows Forms - PullRequest
5 голосов
/ 14 октября 2011

Я пытаюсь написать приложение, которое постоянно ищет хост на локальной сети.Когда я запускаю это как консоль в качестве обратного отсчета. Кажется, что Wait () работает нормально.Однако, когда я перенесу код в форму окна, обратный отсчет. Кажется, что 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 ]

6 голосов
/ 14 октября 2011

Да, ваш код блокируется при использовании его в проекте Winforms. Проблема в том, что класс Ping делает все возможное, чтобы вызвать событие PingCompleted в том же потоке, который вызвал SendAsync (). Для этого он использует метод AsyncOperationManager.CreateOperation ().

Проблема в том, что на самом деле работает в приложении Winforms. Он пытается поднять событие в главном потоке. Но это не может работать, так как вы заблокировали основной поток с помощью вызова countdown.Wait (). Пинг не может быть завершен, так как основной поток заблокирован. Основной поток не может завершиться, так как проверка не завершена. Город тупик.

Он работает в приложении в режиме консоли, поскольку у него нет поставщика синхронизации, как в Winforms. Событие PingComplete будет инициировано в потоке потоков.

Блокировка потока пользовательского интерфейса в корне ошибочна. Быстрое решение заключается в запуске кода в рабочем потоке. Помните, что это делает событие ProbeCompleted сработавшим также и для этого работника. Используйте Control.BeginInvoke (), чтобы перенаправить его в поток пользовательского интерфейса. Или используйте 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 ().

0 голосов
/ 14 октября 2011

ваш обработчик ожидания запускается под потоком из пула потоков. вам нужно вернуться в поток пользовательского интерфейса для обновления пользовательского интерфейса (из-за цикла сообщений, на котором работает пользовательский интерфейс) - для этого вы используете SynchronizationContext

Вот больше информации о том, как вам это: http://www.codeproject.com/KB/threads/SynchronizationContext.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...