在创建位置的其他线程中使用 BitmapDecoder。 (调用线程无法访问此对象,因为另一个线程拥有它。)

Use BitmapDecoder in an other thread then where is created. (The calling thread cannot access this object because a different thread owns it.)

所以,我有一个函数可以在另一个线程中从磁盘异步加载图像(将加载大图像,我不希望 UI 线程在加载时被锁定)。

加载是这样完成的

  public override void LoadFile()
        {
            using (var imageStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                Decoder = new TiffBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                InitializeFile();
            }
        }

然后我想在主线程上使用解码器

   public List<ThumbnailModel> LoadPages()
        {
            var result = new List<ThumbnailModel>();

            foreach (var frame in Decoder.Frames) <--// this line throws exception
            {
                result.Add(new ThumbnailModel
                {
                    Name = _metadataLoader.GetPageName((BitmapMetadata)frame.Metadata),
                    Bitmap = new WriteableBitmap(frame)
                });
            }

            return result;
        }

现在问题来了,每当我到达尝试访问 Decoder.Frames 的那一行时,它就会抛出异常(调用线程无法访问该对象,因为另一个线程拥有它。)

有没有办法在主线程中使用我的解码器,如果没有,唯一可能的解决方案是在另一个线程中加载所有图像信息?

完整代码版本: // 这是调用 imageFactory LoadFile 方法的任务 - NewThread

  private async Task OpenFileAsync(string strFilePath)
            {
                var newFile = _imageFileFactory.LoadFile(strFilePath);

            if (newFile != null)
            {
                _imagefile = newFile;
            }
        }
  //image factory load file - NewThread
  public IImageFile LoadFile(string filePath)
        {
            if (string.IsNullOrWhiteSpace(filePath))
            {
                return null;
            }

            var fileExtension = Path.GetExtension(filePath); // .tiff or .jpeg

            var file = new ImageFileTiff(filePath, _metatadaFactory, _metadataVersioner);

            file.LoadFile();


            return file;
        }
// ImageFileTiff LoadFile will create a decoder - NewThread
 public override void LoadFile()
        {
            using (var imageStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                Decoder = new JpegBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);

                InitializeFile();
            }
        }

在我们有一个 IImageFile 之后,我们调用 MainThread(UIThread)

var pages = _imagefile.LoadPages(); 

其中 LoadPages 是应用程序中断的地方。还调用了 UIThread public 列出 LoadPages() { var result = new List();

        foreach (var frame in Decoder.Frames)
        {
            result.Add(new ThumbnailModel
            {
                Name = _metadataLoader.GetPageName((BitmapMetadata)frame.Metadata),
                Bitmap = new WriteableBitmap(frame)
            });
        }

        return result;
    }

我认为您可以简单地从线程 return 解码器来访问它,但您的解码器是一个继承自 DispatcherObject (https://docs.microsoft.com/en-gb/dotnet/api/system.windows.threading.dispatcherobject?view=netcore-3.1) 的 TiffBitmapDecoder。

因此您将无法从与创建它的线程不同的线程访问它 msdn:"只有创建 Dispatcher 的线程可以直接访问 DispatcherObject"

您可以改为在其线程中使用解码器,return最终结果:

我无法在你的示例上构建,因为我缺少太多测试它但我构建了一个类似的项目来举个例子:

public partial class MainWindow : Window
{
    public MainWindow()
    {

    }

    public TiffBitmapDecoder LoadFile()
    {
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.InitialDirectory = "c:\";
        openFileDialog.Filter = "tiff files (*.tif)|*.tif|All files (*.*)|*.*";
        openFileDialog.FilterIndex = 2;
        openFileDialog.RestoreDirectory = true;

        if (openFileDialog.ShowDialog() == true && !string.IsNullOrEmpty(openFileDialog.FileName))
        {
            //I didn't bother to check the file extension since it's just an exemple
            using (var imageStream = openFileDialog.OpenFile())
            {
                return new TiffBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
            }
        }
        else
        {
            //User cancelled
            return null;
        }
    }

    public List<ThumbnailModel> LoadPages(TiffBitmapDecoder decoder)
    {
        //TiffBitmapDecoder" inherits from DispatcherObject/>
        //https://docs.microsoft.com/en-gb/dotnet/api/system.windows.threading.dispatcherobject?view=netcore-3.1
        var result = new List<ThumbnailModel>();
        if (decoder != null)
        {
            try
            {
                foreach (var frame in decoder.Frames)
                {
                    result.Add(new ThumbnailModel
                    {
                        //set the variables
                    });
                }
            }
            catch(InvalidOperationException e)
            {
                MessageBox.Show(e.Message, "Error");
            }
        }
        else
        {
            //Nothing to do
        }
        return result;
    }

    private async Task AsyncLoading()
    {
        this.thumbnailModels = await Task.Run<List<ThumbnailModel>>(() =>
        {
            var decoder = this.LoadFile();
            return this.LoadPages(decoder);
        });
    }

    private List<ThumbnailModel> thumbnailModels = null;

    private async void AsyncLoadingButton_Click(object sender, RoutedEventArgs e)
    {
        await this.AsyncLoading();
    }
}

public class ThumbnailModel
{
}

MainWindow.xaml 的内容以防万一:

<Grid>
    <StackPanel Orientation="Vertical">
        <Button x:Name="NoReturnButton" Margin="10" HorizontalAlignment="Center" Content="Call AsyncLoadingNoReturn" Click="AsyncLoadingButton_Click" />
    </StackPanel>
</Grid>