如何从FileSystemWatcher进行线程安全调用

时间:2014-02-13 15:56:10

标签: c# wpf

我正在编写一个包含两个部分的应用程序,一部分下载数据并将其源列在另一部分正在监视的文件中,每15分钟下载一次数据,因此更新文件时,它会加载文件内容并删除旧数据。我目前有这个代码:

   private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
   {
       try
       {
           fsw.EnableRaisingEvents = false;
           MessageBox.Show("File Changed: " + e.FullPath);
           _times.Clear();

           XmlDocument dataset = new XmlDocument();
           dataset.Load(@"C:\Users\Henry\AppData\Local\{9EC23EFD-F1A4-4f85-B9E9-729CDE4EF4C7}\cache\DATA_RAINOBS\dataset.xml");
           for (int x = 0; x < dataset.SelectNodes("//Times/Time").Count; x++)
           {
               _times.Add(
                   new Time()
                   {
                       Original = dataset.SelectNodes("//Times/Time/@original")[x].InnerText,
                       Display = dataset.SelectNodes("//Times/Time/@display")[x].InnerText,
                       Directory = dataset.SelectNodes("//Times/Time/@directory")[x].InnerText + "_LORES.png"
                   });
           }
           times.SelectedIndex = 0;
       }
       finally { fsw.EnableRaisingEvents = true; }
   }

但是当我运行它时,我得到一个System.NotSupportedException并且从进一步的信息中我知道这是因为我试图从FileSystemWatcher创建的单独线程中操作列表。

我几乎没有使用多个线程进行任何编程,因此我不知道该怎么做。如果有人可以修改上面的代码以便它是线程安全的并且能够正常工作,那将是非常有帮助的,因为那时我将有一些东西需要学习并且它不会出错。我如何使这项工作?

2 个答案:

答案 0 :(得分:1)

您必须在控件(或其所有者)上使用Invoke。然后该方法将排队等待控件的线程处理,而不是FSW的线程。

在WPF上,这是由调度员为此处理的,例如。

times.Dispatcher.Invoke(() => { yourCode; });

如果您希望您的代码需要一些时间,您可能会考虑在结尾处仅使用完整的项目列表进行调用,而不是调用整个操作。

答案 1 :(得分:0)

您的_times集合与GUI部分绑定,因此错误必须在行

_times.Clear();

WPF的约束是您无法修改从UI线程以外的线程绑定到GUI的源集合。有关详细信息,请参阅我对here的回答。

如上所述,您可以仅从UI线程修改它,因此请考虑在UI调度程序上调度此内容,这将在UI线程上对此进行排队。获取这样的UI调度程序:

App.Current.Dispatcher.Invoke((Action)delegate
{
   _times.Clear();
});

还要确保在UI线程上调度与UI相关的任何后续调用。可以只在一个电话下打包:

XmlDocument dataset = new XmlDocument();
dataset.Load(@"C:\Users\Henry\AppData\Local\{9EC23EFD-F1A4-4f85-B9E9-
               729CDE4EF4C7}\cache\DATA_RAINOBS\dataset.xml");
App.Current.Dispatcher.Invoke((Action)delegate
{
   _times.Clear();
   for (int x = 0; x < dataset.SelectNodes("//Times/Time").Count; x++)
   {
      _times.Add(
           new Time()
           {
               Original = dataset.SelectNodes("//Times/Time/@original")
                                [x].InnerText,
               Display = dataset.SelectNodes("//Times/Time/@display")
                                [x].InnerText,
               Directory = dataset.SelectNodes("//Times/Time/@directory")
                                [x].InnerText + "_LORES.png"
           });
   }
   _times.SelectedIndex = 0;
});