在创建位置的其他线程中使用 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>
所以,我有一个函数可以在另一个线程中从磁盘异步加载图像(将加载大图像,我不希望 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>