當遇上了死鎖問題時,急的焦頭爛額。說不定我分享的這篇文章對你有些幫助。
問題描述
在主程中有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