c# - wpf - 在执行Thread期间主UI挂起

时间:2017-03-01 05:49:04

标签: c# wpf multithreading

我正在使用带多线程的WPF。问题是在执行MAIN线程挂起期间,就像在主UI中等待某些东西或无限循环一样。在WPF中涉及多线程时,我不知道是否还有其他方法。请参阅下面的代码:

Thread myNewThread1 = new Thread(() => ping(IP1, img_m));
Thread myNewThread2 = new Thread(() => ping(IP2, img_h));
Thread myNewThread3 = new Thread(() => ping(IP3, img_c));
Thread myNewThread4 = new Thread(() => ping(IP4, img_b));
Thread myNewThread5 = new Thread(() => ping(IP5, img_e));

myNewThread1.Start();
myNewThread2.Start();
myNewThread3.Start();
myNewThread4.Start();
myNewThread5.Start();


private void ping(string IP, Image img)
{
    this.Dispatcher.Invoke(() =>
    {
        Ping p = new Ping();
        var r = p.Send(IP, 1000, new byte[5]);

        if (r.Status == IPStatus.Success)
        {
            var image = new BitmapImage();

            image.BeginInit();
            image.UriSource = new Uri("subonline.gif", UriKind.Relative);
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.EndInit();
            ImageBehavior.SetAnimatedSource(img, image);
        }
        else
        {
            var image = new BitmapImage();

            image.BeginInit();
            image.UriSource = new Uri("suboffline.gif", UriKind.Relative);
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.EndInit();
            ImageBehavior.SetAnimatedSource(img, image);
        }
    });

    Thread.Sleep(500);
    ping(IP, img);
}    

2 个答案:

答案 0 :(得分:1)

您的主线程由于Dispatcher.Invoke错误使用而挂起,它应仅用于UI逻辑,因此您应该移出面向Ping的逻辑。

不要使用BackgroundWorker,这是一个你真正不需要的过时和沉重的结构。另外,不要用于ping的线程,这是错误的方法,这就是原因:

Ping操作与网络相关,您用于等待远程服务器响应的线程只会浪费系统资源,因为它绝对没有除了等待。所以你应该为此切换到异步方法。

您应该订阅Ping.PingCompleted事件并调用SendAsync方法,如下所示:

private void ping(string IP, MediaTypeNames.Image img)
{
    Ping p = new Ping();
    PingReply r;
    // lambda delegate here, may use event handler instead
    p.PingCompleted += (sender, args) => { PingCompleted(args, img); };
    r = p.SendAsync(IP, 1000, new byte[5], null);
}

private void PingCompleted(PingCompletedEventArgs args, MediaTypeNames.Image img)
{
    this.Dispatcher.Invoke(() =>
        {
            string imageAddress;
            if (args.Reply.Status == IPStatus.Success)
            {
                imageAddress = "subonline.gif";
            }
            else
            {
                imageAddress = "suboffline.gif";
            }

            var image = new BitmapImage();
            image.BeginInit();
            image.UriSource = new Uri(imageAddress, UriKind.Relative);
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.EndInit();
            ImageBehavior.SetAnimatedSource(img, image);
        });
}

或者您应该使用async/await功能,该功能是针对此类情况引入的(代码部分来自this answer):

// async event handler
private async void btn_Click(object sender, EventArgs e)
{
    // async call to all the ips
    var results = await PingAsync(new List<string> { IP1, IP2, IP3, IP4, IP5 });
    // here we do not need the Dispatcher as await will restore UI context for you
   PingCompleted(results[0], img_m);
   // etc
}

private void PingCompleted(PingReply r, MediaTypeNames.Image img)
{
    string imageAddress;
    if (r.Status == IPStatus.Success)
    {
        imageAddress = "subonline.gif";
    }
    else
    {
        imageAddress = "suboffline.gif";
    }

    var image = new BitmapImage();
    image.BeginInit();
    image.UriSource = new Uri(imageAddress, UriKind.Relative);
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.EndInit();
    ImageBehavior.SetAnimatedSource(img, image);
}

// helper method
private async Task<PingReply[]> PingAsync(List<string> theListOfIPs)
{
    Ping pingSender = new Ping();
    var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 1000, new byte[5]));
    return await Task.WhenAll(tasks);
}

答案 1 :(得分:0)

不要在调度程序中包含对p.Send方法的调用 - 当前只有Thread.Sleep(500)在后台完成,其他所有操作都在UI线程中完成。

另外,我相信由于Thread.Sleep,函数永远不会给UI线程一个工作的机会,所以ping不会发生。将Thread.Sleep替换为Thread.Timer对象。