当遇上了死锁问题时,急的焦头烂额。说不定我分享的这篇文章对你有些帮助。
问题描述
在主程中有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