ArgumentOutOfRangeException при доступе к коллекции, даже если индекс не выходит за пределы диапазона? - PullRequest
1 голос
/ 25 июня 2011

У меня есть приложение, которое обрабатывает изображения через серию фильтров изображений в одном потоке и отображает изображения в пользовательском интерфейсе в другом потоке. Упрощенный класс фильтра изображений выглядит следующим образом:

// 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;
    }
}

Кроме того, у меня есть различные производные классы, которые отличаются тем, что шейдерная программа HLSL выполняется на GPU. Поток обработки изображений перебирает коллекцию этих экземпляров фильтра. GetEffectResult () вызывается только тогда, когда необходимо загрузить изображение из видеопамяти в системную память. Я использую Monitor.Enter (this), потому что каждый экземпляр фильтра гарантированно существует только один раз в цепочке фильтров.

Чтобы установить и настроить фильтры, у меня есть пользовательский интерфейс, который отображает выходные данные каждого фильтра в цепочке фильтров. Экземпляры фильтра инкапсулированы моделями фильтров, которые используются пользовательским интерфейсом WPF.

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 () каждого экземпляра модели фильтра неоднократно вызывается потоком пользовательского интерфейса через таймер.

Время от времени я получаю ArgumentOutOfRangeException в следующей строке:

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

Однако, когда я проверяю свойство LastResults, оно содержит один элемент, как и должно быть. Как это возможно, что ArgumentOutOfRangeException вызывается, даже если отладчик говорит, что коллекция на самом деле содержит только один элемент, и я всегда обращаюсь к первому элементу в коллекции?

1 Ответ

3 голосов
/ 25 июня 2011

Свойство повторно оценивается отладчиком после исключения после того, как было сгенерировано.Возможно, этот элемент был вставлен другим потоком, пока Visual Studio останавливает все для отладчика.

HTH

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...