先描述一下需求,服务器程序需要对终端的在线状态进行跟踪,采用ping的发放获取信息,在ping通或者ping不通后需要对UI进行更新,下面是出问题的代码(此例ping的方法比较简单,尚未进行应用测试勿用作实际用途):
1 public partial class MainWindow : Window
2 {
3 public MainWindow()
4 {
5 InitializeComponent();
6 }
7 List<string> ClientInfoList = new List<string>();
8 private void Window_Loaded(object sender, RoutedEventArgs e)
9 {
10 for (int i = 1; i < 50; i++)
11 {
12 ClientInfoList.Add("192.168.1." + i.ToString());
13 }
14
15 ThreadPool.QueueUserWorkItem(
16 delegate
17 {
18 while (true)
19 {
20 Debug.WriteLine(DateTime.Now.ToString() + "Ping");
21 int OnLineCout = 0;
22 Ping pingSender = new Ping();
23 PingOptions options = new PingOptions();
24 for (int i = 0; i < ClientInfoList.Count; i++)
25 {
26 if (ClientInfoList[i] != "")
27 {
28 options.DontFragment = true;
29 bool PingSuceess = false;
30 PingReply reply = pingSender.Send(ClientInfoList[i], 100);
31 if (reply.Status == IPStatus.Success)
32 {
33 PingSuceess = true;
34 }
35
36 if (!PingSuceess)
37 {
38 Debug.WriteLine(DateTime.Now.ToString() + "Ping失败,终端IP:" + ClientInfoList[i]);
39 this.Dispatcher.BeginInvoke((Action)delegate()
40 {
41 label1.Content = ClientInfoList[i];
42 });
43 }
44 else
45 {
46 OnLineCout++;
47 }
48
49 }
50 }
51 Debug.WriteLine(DateTime.Now.ToString() + "PingOver");
52 Thread.Sleep(3000);
53 }
54 }, null);
55
56 }
57
58 private void button1_Click(object sender, RoutedEventArgs e)
59 {
60 button1.Content = new Random().Next();
61 }
62 }
第39行为更新UI的方法,当循环执行完毕后执行第二次循环后,会不定期出现程序异常。实际的应用比这个例子代码要复杂很多,所以不断的注释,使代码简化,最终将问题锁定在本例中的39行附近,后来一直是怀疑不该使用this.Dispatcher.BeginInvoke这种方式来实现。再后来采用DispatcherTimer类来实现,由于程序其它线程也要操作一些UI对象,使用这种方式又导致UI响应太慢,出现卡顿现象(再多的DispatcherTimer都使用一个线程来调度,所以DispatcherTimer开的越多,UI越卡)。再后来找到了老赵的帖子《
警惕匿名方法造成的变量共享》受到启发,开始怀疑是匿名实现this.Dispatcher.BeginInvoke
造成的,后将代码改为以下:
1 public partial class MainWindow : Window
2 {
3 public MainWindow()
4 {
5 InitializeComponent();
6 }
7 List<string> ClientInfoList = new List<string>();
8 delegate void UpdateUI(string str);
9
10 void UpdateUIMethod(string str)
11 {
12 label1.Content = str;
13 }
14 private void Window_Loaded(object sender, RoutedEventArgs e)
15 {
16 for (int i = 1; i < 50; i++)
17 {
18 ClientInfoList.Add("192.168.1." + i.ToString());
19 }
20 UpdateUI UUI = new UpdateUI(UpdateUIMethod);
21 ThreadPool.QueueUserWorkItem(
22 delegate
23 {
24 while (true)
25 {
26 Debug.WriteLine(DateTime.Now.ToString() + "Ping");
27 int OnLineCout = 0;
28 Ping pingSender = new Ping();
29 PingOptions options = new PingOptions();
30 for (int i = 0; i < ClientInfoList.Count; i++)
31 {
32 if (ClientInfoList[i] != "")
33 {
34 options.DontFragment = true;
35 bool PingSuceess = false;
36 PingReply reply = pingSender.Send(ClientInfoList[i], 100);
37 if (reply.Status == IPStatus.Success)
38 {
39 PingSuceess = true;
40 }
41
42 if (!PingSuceess)
43 {
44 Debug.WriteLine(DateTime.Now.ToString() + "Ping失败,终端IP:" + ClientInfoList[i]);
45 //this.Dispatcher.BeginInvoke((Action)delegate()
46 // {
47 // label1.Content = ClientInfoList[i];
48 // });
49 this.Dispatcher.BeginInvoke(UUI, ClientInfoList[i]);
50 }
51 else
52 {
53 OnLineCout++;
54 }
55
56 }
57 }
58 Debug.WriteLine(DateTime.Now.ToString() + "PingOver");
59 Thread.Sleep(3000);
60 }
61 }, null);
62
63 }
64
65 private void button1_Click(object sender, RoutedEventArgs e)
66 {
67 button1.Content = new Random().Next();
68 }
69 }