【问题标题】:Allow main (gui) thread while doing a task then update table在执行任务时允许主(gui)线程然后更新表
【发布时间】:2018-10-23 06:28:36
【问题描述】:

我正在使用 winform 尝试收集网络上每个 IP 的在线/离线状态,这样做我正在使用 ping 请求,如果它回复它会将 IP 标记为在线,然后继续。唯一的问题是等待最多 255 个回复,在获得所有 255 个回复后,我希望它填充数据网格视图。

我已经设法让它全部工作,但唯一的缺点是,gui 在执行此过程时挂起,我想只需使用具有以下表达式的单独线程

Thread T1 = new Thread(PingHost)
T1.run();

PingHost 顾名思义,ping 所有主机并决定是在线还是离线。问题是我试图从线程更新 dgv 表,自然 dgv 被锁定到主线程。

所以我尝试切换到一个任务,并在一切完成后获取返回值并更新。遗憾的是,我不太明白如何从任务中获取返回值并让它在单独的线程上运行。

我试过用谷歌搜索不同的方法,但现在只是在兜圈子,所以我很谦虚地向你们寻求帮助

使用任务的主线程代码

List<string> everything = new List<string>();

        int i = 0;


        Task<List<string>> task1 = Task<List<string>>.Factory.StartNew(PingHost);
        everything = task1.Result;
        foreach(var item in everything)
        {
            var index = dataGridView1.Rows.Add();
            dataGridView1.Rows[i].Cells["IP"].Value = item;
            i++;
        }

这是我的 PingHost 方法的线程

bool pingable = false;
        Ping pinger = null;
        int i = 0;

        string ip;
        while (i < 255)
        {
             ip = "192.168.0." + i;
            try
            {
                pinger = new Ping();
                PingReply reply = pinger.Send(ip, 8);
                pingable = reply.Status == IPStatus.Success;
            }
            catch (PingException)
            {
                MessageBox.Show("ERROR");
                // Discard PingExceptions and return false;
            }
            finally
            {
                if (pinger != null)
                {
                    pinger.Dispose();
                }
            }
            if (pingable)
            {
                checkedIP.Add(ip + ": ONLINE");
            }
            else
            {
            checkedIP.Add(ip + ": OFFLINE");

            }
            i++;
        }
        return checkedIP;

【问题讨论】:

  • 使用任务是不够的。您需要使用“异步/等待”。使用“.Result”只是阻塞,因此不是你想要的。

标签: c# multithreading datagridview task


【解决方案1】:

这可能有点矫枉过正,但我​​只是草拟了一个解决方案。基本上,我创建了一个新的类,用于在每次 Ping 返回后触发的事件进行 ping,此事件使用自定义 EventArgs 返回刚刚被 ping 的 IP 以及它是否在线。然后我在我的 GUI 线程中订阅了该事件并更新了 GUI。这是一些代码:

这是我负责 ping 实际计算机的 Pinger 类:

class Pinger
{
    public event EventHandler<PingReturnedEventArgs> OnPingReturned;

    public void PingNetwork()
    {
        for (int i = 1; i <= 255; i++)
        {
            string ip = $"192.168.0.{i}";

            Ping ping = new Ping();

            try
            {
                PingReply reply = ping.Send(IPAddress.Parse(ip));
                TriggerEvent(reply?.Address.ToString(), true);
            }
            catch (Exception)
            {
                TriggerEvent(ip, false);
            }
        }
    }

    private void TriggerEvent(string ip, bool online)
    {
        if (OnPingReturned == null) return;

        PingReturnedEventArgs args = new PingReturnedEventArgs(ip, online);
        OnPingReturned(this, args);
    }
}

我的自定义 EventArgs:

class PingReturnedEventArgs : EventArgs
{
    public string Ip { get; private set; }

    public bool Online { get; private set; }

    private PingReturnedEventArgs() { }

    public PingReturnedEventArgs(string ip, bool online)
    {
        Ip = ip;
        Online = online;
    }
}

最后,这是我实际使用所有这些的方式:

Pinger pinger = new Pinger();
pinger.OnPingReturned += Your event handler to update the GUI

// I recommend doing it like this so you can stop the Thread at a later time
// Maybe with like a cancel button
Thread pingThread = new Thread(pinger.PingNetwork);
pingThread.Start();

事件处理程序看起来像这样private void PingReturnedHandler(object sender, PingReturnedEventArgs args)

这样做的两个主要好处是 1. GUI 线程保持畅通,这意味着 GUI 仍将响应用户输入;以及 2. 这在程序上(在每次 ping 完成时)触发事件,这意味着如果需要很长时间是时候 ping 所有的 PC 了,您不必等待全部完成后才能看到用户看到的东西

【讨论】:

  • 由于某种原因在运行时,PingReturnedEventArgs 卡在 192.168.0.8 上,即使它只是 ping 192.168.0.4
  • 你能给我更多的信息吗?
猜你喜欢
  • 2019-04-04
  • 1970-01-01
  • 2011-08-05
  • 2019-08-18
  • 2017-06-16
  • 2013-10-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
相关资源
最近更新 更多