访问集合时出现ArgumentOutOfRangeException,即使索引未超出范围

本文关键字:索引 范围 集合 ArgumentOutOfRangeException 访问 | 更新日期: 2023-09-27 18:00:16

我有一个应用程序,它在一个线程中通过一系列图像过滤器处理图像,并在另一个线程的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);
        }
    }

UI线程通过Timer重复调用每个过滤器模型实例的Refresh()方法。

偶尔,我会在下面的行中收到一个ArgumentOutOfRangeException:

filterImage = this.filter.LastResults.ElementAtOrDefault(0);

然而,当我检查LastResults属性时,它包含一个元素,就像它应该包含的一样。即使调试器说集合实际上只包含一个项,而我总是访问集合中的第一个项,但怎么可能引发ArgumentOutOfRangeException呢?

访问集合时出现ArgumentOutOfRangeException,即使索引未超出范围

在抛出异常后,调试器将重新评估属性。可能是在visualstudio为调试器停止所有操作时,另一个线程插入了该元素。

HTH