【问题标题】:ArgumentOutOfRangeException when accessing a Collection, even though index is not out of range?访问集合时出现 ArgumentOutOfRangeException,即使索引没有超出范围?
【发布时间】: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


【解决方案1】:

调试器抛出异常之后重新评估该属性。可能该元素已被另一个线程插入,而 Visual Studio 正在为调试器停止一切。

HTH

【讨论】:

  • 谢谢,我不知道调试器会重新评估属性。这可能解释了我在标题中提出的问题,为什么可能发生 ArgumentOutOfRange 异常,即使调试器稍后说集合中有一个项目。我还不明白 LastResults 返回的集合如何不包含任何项目(如果您查看属性的定义,它会在每次调用该属性时无条件地向集合中添加一项)。
猜你喜欢
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 2022-01-13
  • 2020-01-14
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多