多線程操作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 

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