多线程操作WriteableBitmap 复制源数据使用 CopyPixels 遇到死锁

当遇上了死锁问题时,急的焦头烂额。说不定我分享的这篇文章对你有些帮助。

问题描述

在主程中有WriteableBitmap,开了分线程不断去write对应的bitmap然后更新到界面形成有效的视频流。多个视频流同时高像素高效率的运行时,碰撞出了保存图像的火花。

增添一个保存图像的功能,这时需要把这个writeablebitmap拷贝一份出来,然后做save操作,尽量减少视频流的影响。

问题分析

当时使用函数CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride),随后又尝试了CopyPixels (Array pixels, int stride, int offset)均以失败告终。有找过.net源码一步步进行深入分析,再者用替换方法,代替实现反而成功了。

1.CopyPixels源码分析

        [SecurityCritical]
        [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public virtual void CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride)
        {
            ReadPreamble();
            EnsureShouldUseVirtuals();
            _bitmapInit.EnsureInitializedComplete();
            CompleteDelayedCreation();
 
            // Demand Site Of origin on the URI if it passes then this  information is ok to expose
            CheckIfSiteOfOrigin();
 
            CriticalCopyPixels(sourceRect, buffer, bufferSize, stride);
        }

ReadPreamble其实就是检查线程是否拥有访问权

EnsureShouldUseVirtuals是确保使用了虚拟技术,CopyPixels还用到了虚拟技术?好像有点腻害的样子

EnsureInitializedComplete是 确保Bitmap已经初始化

CompleteDelayedCreation是 是否延迟执行,可能抛了异常,所以停止执行然后继续抛

        internal bool DelayCreation
        {
            get
            {
                return _delayCreation;
            }
            set
            {
                _delayCreation = value;
 
                if (_delayCreation)
                {
                    CreationCompleted = false;
                }
            }
        }

        ///
        /// Demand that the bitmap should be created if it was delay-created.
        ///
        internal void CompleteDelayedCreation()
        {
            // Protect against multithreaded contention on delayed creation.
            if (DelayCreation)
            {
                lock (_syncObject)
                {
                    if (DelayCreation)
                    {
                        EnsureShouldUseVirtuals();
 
                        DelayCreation = false;
 
                        try
                        {
                            FinalizeCreation();
                        }
                        catch
                        {
                            DelayCreation = true;
                            throw;
                        }
 
                        CreationCompleted = true;
                    }
                }
            }
        }

CheckIfSiteOfOrigin是一些强制保护措施

CriticalCopyPixels是重点,小二,上代码给各位客官品尝品尝

        /// <summary>
        /// CriticalCopyPixels
        /// </summary>
        /// <param name="sourceRect"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferSize"></param>
        /// <param name="stride"></param>
        /// <SecurityNote>
        ///     Critical: There is a risk of buffer overruns when copying data to the buffer.
        ///               This code can also be used to expose pixel information
        /// </SecurityNote>
        [SecurityCritical]
        internal void CriticalCopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride)
        {
            if (buffer == IntPtr.Zero)
                throw new ArgumentNullException("buffer");
 
            if (stride <= 0)
                throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterMustBeGreaterThanZero));
 
            if (sourceRect.Width <= 0)
                sourceRect.Width = PixelWidth;
 
            if (sourceRect.Height <= 0)
                sourceRect.Height = PixelHeight;
 
            if (sourceRect.Width > PixelWidth)
                throw new ArgumentOutOfRangeException("sourceRect.Width", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelWidth));
 
            if (sourceRect.Height > PixelHeight)
                throw new ArgumentOutOfRangeException("sourceRect.Height", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelHeight));
 
            int minStride = checked(((sourceRect.Width * Format.BitsPerPixel) + 7) / 8);
            if (stride < minStride)
                throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, minStride));
 
            int minRequiredDestSize = checked((stride * (sourceRect.Height - 1)) + minStride);
            if (bufferSize < minRequiredDestSize)
                throw new ArgumentOutOfRangeException("buffer", SR.Get(SRID.ParameterCannotBeLessThan, minRequiredDestSize));
 
            lock (_syncObject)
            {
                HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.CopyPixels(
                    WicSourceHandle,
                    ref sourceRect,
                    (uint)stride,
                    (uint)bufferSize,
                    buffer
                    ));
            }
        }

两次用到了lock(_syncObject)这个操作,试试其他几个重载函数都遇到了死锁问题,看来copypixels对多线程的支持不是很好,问题得不到有效解决。

2.替换为Marshal.Copy()

当不使用CopyPixels,而去使用Marshal.Copy()的时候,问题立马得到了有效解决。

> 从WriteableBitmap拿到数据长度,new byte[]出来

> 使用Marshal.Copy把WriteableBitmap的数据源拷贝到byte[]

> 使用得到的byte[]再new一个WriteableBitmap出来,即为问题得到解决

 

问题总结

有时候解决问题,并不是为了一定要用一个办法,既然知道走不通那我们就换条路走。

 

撰写不易,转发请加注作者以及网址

URL: https://blog.csdn.net/coconut9325/article/details/96152983

CSDN : CocoLin 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章