【问题标题】:How to make thread safe calls from FileSystemWatcher如何从 FileSystemWatcher 进行线程安全调用
【发布时间】:2014-02-14 00:43:12
【问题描述】:

我正在编写一个包含两部分的应用程序,一部分下载数据并将其来源列出在一个文件中,该文件由另一部分监控,每 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 创建的单独线程中操作一个列表。

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

【问题讨论】:

  • 哪一行导致异常?
  • 错误中突出显示的行是 XmlDocument 的创建: XmlDocument dataset = new XmlDocument();错误是:这种类型的collectionview不支持从不同线程更改其sourcecollection
  • @HenryHunt - 在此处发布您收到的实际异常消息。
  • @RohitVats 在 PresentationFramework.dll 中发生“System.NotSupportedException”类型的未处理异常附加信息:这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection。跨度>
  • @HenryHunt - 我已经发布了答案。请检查。

标签: c# wpf


【解决方案1】:

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

在 WPF 上,这是由调度程序处理的,例如。

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

如果您希望您的代码花费一些时间,您可以考虑只在最后执行包含完整项目列表的调用,而不是调用整个操作。

【讨论】:

  • 在 WinForms 中,您使用 Control.Invoke() dailycoding.com/Posts/…
  • 这将解决我在使用控件时遇到的问题,但错误出现在创建 XmlDocument 的行中。为什么是这样?不能在另一个线程上创建 XmlDocument 吗?
  • @HenryHunt 你确定吗?不是times.Clear上的错误吗? MessageBox.Show 在不同的线程中也不是一个好主意。
  • @Luaan 它可能在 times.Clear() 但 times.Clear() 实际上是 _times.Clear() 而 _times 是一个变量(List
  • @HenryHunt 啊,对...但是,我没有看到您在实际的times 控件上设置数据源,它已经分配了吗?这可能意味着更改 _times 实际上也会更改控件。只是一个想法。
【解决方案2】:

您的 _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;
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多