异步函数与静态类

时间:2016-12-11 04:46:25

标签: c#

我有一个静态类,其中包含文件扩展名和BitMapSource对象的字典。该类包含一个函数,给定一个FileInfo对象将返回关联的BitMapSource对象,如果它不在字典中,它将获取它并在返回之前将其放入字典中。
从GUI线程执行时,这很好用。但是,当我尝试将它放在后台线程中时,我没有得到任何回报。我有什么理由不能从后台线程执行此操作吗?

静态类

namespace Test.Classes
{
  using System.Collections.Generic;
  using System.Drawing;
  using System.IO;
  using System.Windows;
  using System.Windows.Interop;
  using System.Windows.Media.Imaging;

  public static class IconMap
  {
    private static Dictionary<string, BitmapSource> iconDictionary = new Dictionary<string, BitmapSource>();

    public static BitmapSource GetFileIcon(FileInfo fileInfo)
    {
      if (iconDictionary.ContainsKey(fileInfo.Extension))
      {
        return iconDictionary[fileInfo.Extension];
      }
      else
      {
        lock (iconDictionary)
        {
          Icon icon = Icon.ExtractAssociatedIcon(fileInfo.FullName);
          BitmapSource bitMapSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
          iconDictionary.Add(fileInfo.Extension, bitMapSource);
          return bitMapSource;
        }
      }
    }
  }
}

Control.cs

namespace Test.Controls
{
  using System;
  using System.Diagnostics;
  using System.IO;
  using System.Threading.Tasks;
  using System.Windows.Controls;
  using System.Windows.Input;
  using System.Windows.Media.Imaging;
  using Microsoft.Office.Interop.Outlook;

  public partial class AttachedFileInfo : UserControl
  {
    private FileInfo file;

    public AttachedFileInfo(FileInfo fileInfo)
    {
      this.InitializeComponent();
      this.file = fileInfo;

      this.FileLink.NavigateUri = new Uri(fileInfo.FullName);
      this.FileName.Text = fileInfo.Name;
      this.LoadFileIcon(fileInfo);
    }

    private async void LoadFileIcon(FileInfo fileInfo)
    {
      Task<BitmapSource> getFileIconTask = Task<BitmapSource>.Factory.StartNew(() =>
      {
        // If I change this to BitmapSource icon = null; it works as expected.
        BitmapSource icon = Classes.IconMap.GetFileIcon(fileInfo);
        return icon;
      });
      await getFileIconTask;

      this.FileIcon.Source = Classes.IconMap.GetFileIcon(fileInfo); 
      // getFileIconTask.Result;
    }

    private void FileLink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
    {
      Process.Start(this.file.FullName);
    }
  }
}

1 个答案:

答案 0 :(得分:2)

您的图标不会显示,因为只有通过调用.Freeze()方法冻结WPF资源才能使用其他线程(请参阅下面的代码)。

另一个问题是即使你使用ConcurrentDictionary,你的GetFileIcon()方法也不是线程安全的,如果一个线程向iconDictionary添加了图标,当它离开锁定的代码块时,另一个线程可能会进入锁定块将相同文件类型的图标添加到字典中,从而导致ArgumentException。因此,在锁定内,您必须再次测试字典中是否已存在特定扩展名的图标。

private static ConcurrentDictionary<string, BitmapSource> iconDictionary = new ConcurrentDictionary<string, BitmapSource>();

public static BitmapSource GetFileIcon(FileInfo fileInfo)
{
    BitmapSource bitMapSource;
    if (iconDictionary.TryGetValue(fileInfo.Extension, out bitMapSource))
    {
        return bitMapSource;
    }
    else
    {
        lock (iconDictionary)
        {
            if (iconDictionary.TryGetValue(fileInfo.Extension, out bitMapSource))
                return bitMapSource;

            Icon icon = Icon.ExtractAssociatedIcon(fileInfo.FullName);
            bitMapSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
            bitMapSource.Freeze();//Allows BitmapSource to be used on another thread
            iconDictionary[fileInfo.Extension] = bitMapSource;
            return bitMapSource;
        }
    }
}