【发布时间】:2011-06-25 14:52:25
【问题描述】:
我有一个应用程序,它通过一个线程中的一系列图像过滤器处理图像,并在另一个线程的 UI 中显示图像。简化的图像过滤器类如下所示:
// This class represents a filter operation on the GPU.
// Process() is repeatedly called by the filter processing thread.
class ShaderFilterBase : IFilter
{
// This variable holds the result of the image operation.
private ImageInfo resultImageInfo;
// Indicates whether the image has been updated by a call to Render()
bool hasChanged = true;
public ICollection<ImageInfo> LastResults
{
get
{
ICollection<ImageInfo> results = new Collection<ImageInfo>();
results.Add(GetEffectResult());
return results;
}
}
public ICollection<ImageInfo> Process(ICollection<ImageInfo> images)
{
// We only process the image if we have exactly one image.
// If more than one image has to be processed by the GPU, this method
// should be overridden in a derived class.
if (images.Count == 1)
{
ImageInfo firstImage = images.First();
// If the supplied image is already a shader resource on the GPU,
// we don't need to upload the texture to the GPU again. We just
// set the output texture of the supplied image to the input texture
// of the current image.
if (firstImage.IsShaderResource)
SetResource(firstImage.ShaderResourceView);
else
UploadInputTexture(firstImage);
Render();
firstImage.ShaderResourceView = OutputShaderResourceView;
}
return images;
}
public virtual void Render()
{
Monitor.Enter(this);
// Perform texture operations on the GPU
hasChanged = true;
Monitor.Exit(this);
}
public ImageInfo GetEffectResult()
{
Monitor.Enter(this);
if (hasChanged)
{
// Download image from GPU and store it in resultImageInfo
hasChanged = false;
}
Monitor.Exit(this);
return resultImageInfo;
}
}
此外,我有各种不同的派生类,它们因要在 GPU 上执行的 HLSL 着色器程序而异。图像处理线程遍历这些过滤器实例的集合。 GetEffectResult() 仅在图像需要从显存下载到系统内存时调用。我使用 Monitor.Enter(this),因为每个过滤器实例都保证在过滤器链中只存在一次。
为了设置和配置过滤器,我有一个 UI,它显示过滤器链中每个过滤器的输出。过滤器实例由过滤器模型封装,由 WPF UI 使用。
internal abstract class FilterModelBase : DependencyObject
{
private WriteableBitmap preview;
private static readonly DependencyPropertyKey PreviewPropertyKey = DependencyProperty.RegisterReadOnly("Preview",
typeof(ImageSource), typeof(FilterModelBase), new PropertyMetadata());
// The WPF window contains an Image control, which binds to this property.
public static readonly DependencyProperty PreviewProperty = PreviewPropertyKey.DependencyProperty;
public ImageSource Preview
{
get { return (ImageSource)GetValue(PreviewProperty); }
private set { SetValue(PreviewPropertyKey, value); }
}
// The underlying filter.
public IFilter Filter
{
get { return this.filter; }
}
protected FilterModelBase(IEventAggregator eventAggregator, IFilter filter)
{
Check.NotNull(filter, "filter");
this.EventAggregator = eventAggregator;
this.filter = filter;
}
// Updates the filter output preview.
public virtual void Refresh(Dispatcher dispatcher)
{
if (!dispatcher.CheckAccess())
dispatcher.Invoke(new Action(() => Refresh(dispatcher)));
else
{
ImageInfo filterImage = null;
Monitor.Enter(this.filter);
if (this.filter != null && this.filter.LastResults.Count > 0)
filterImage = this.filter.LastResults.ElementAtOrDefault(0);
if (filterImage != null)
{
this.preview.WritePixels(new Int32Rect(0, 0, filterImage.Width, filterImage.Height),
filterImage.ImageBytes, filterImage.Width * filterImage.Channels, 0);
}
Monitor.Exit(this.filter);
}
}
每个过滤器模型实例的 Refresh() 方法由 UI 线程通过 Timer 重复调用。
时不时地,我会在以下行收到 ArgumentOutOfRangeException:
filterImage = this.filter.LastResults.ElementAtOrDefault(0);
但是,当我检查 LastResults 属性时,它包含一个元素,就像它应该的那样。怎么可能引发 ArgumentOutOfRangeException,即使调试器说 Collection 实际上只包含一个项目并且我总是访问集合中的第一个项目?
【问题讨论】:
-
挑衅性的标题,虽然我倾向于相信例外是正确的,而不仅仅是拉你的腿。应用程序是多线程的吗?
-
时不时地 == 线程竞赛。
-
@Micheal:您尝试过 this.filter.LastResults[0],如果您已经尝试过,请在引发异常后尝试在快速午餐窗口重新执行命令,这样您就可以确保在抛出异常后调试器不会重新评估该属性,如 HTH 所说..
-
不尝试就使用 Monitor..finally 是有风险的。考虑改用 lock 语句。此外,如果实例可以公开访问,请避免锁定
this。
标签: c# multithreading collections