C#+无unsafe的非托管大数组示例详解(large unmanaged array in c# without ‘unsafe’ keyword)
C#申请一个大数组(UsealargearrayinC#)
在C#里,有时候我需要能够申请一个很大的数组、使用之、然后立即释放其占用的内存。
SometimesIneedtoallocatealargearray,useitandthenreleaseitsmemoryspaceimmediately.
由于在C#里提供的int[]array=newint[1000000];这样的数组,其内存释放很难由程序员完全控制,在申请一个大数组后,程序可能会变得很慢。
IfIusesomethinglike int[]array=newint[1000000];,itwillbedifficulttoreleaseitsmemoryspacebyprogrammerandtheappprobablyrunsslowerandslower.
特别是在C#+OpenGL编程中,我在使用VAO/VBO时十分需要设计一个非托管的数组,比如在glBufferData时我希望可以使用下面的glBufferData:
SpeciallyinC#+OpenGLroutineswhenI'musingVAO/VBO,IneedanunmanagedarrayforglBufferData:
//////设置当前VBO的数据。 /// ////// /// publicstaticvoidglBufferData(uinttarget,UnmanagedArrayBasedata,uintusage) { GetDelegateFor ()((uint)target, data.ByteLength,//使用非托管数组 data.Header,//使用非托管数组 (uint)usage); } //... //glBufferData的声明 privatedelegatevoidglBufferData(uinttarget,intsize,IntPtrdata,uintusage);
而在指定VBO的数据时,可能是float、vec3等等类型:
AndthecontentinVBOcanbefloat,vec3andanyotherstructs.
//////金字塔的posotionarray. /// staticvec3[]positions=newvec3[] { newvec3(0.0f,1.0f,0.0f), newvec3(-1.0f,-1.0f,1.0f), //... newvec3(-1.0f,-1.0f,1.0f), }; //Createavertexbufferforthevertexdata. { uint[]ids=newuint[1]; GL.GenBuffers(1,ids); GL.BindBuffer(GL.GL_ARRAY_BUFFER,ids[0]); //使用vec3作为泛型的非托管数组的参数 UnmanagedArraypositionArray=newUnmanagedArray (positions.Length); for(inti=0;i UnmanagedArray
所以我设计了这样一个非托管的数组类型:无unsafe,可接收任何struct类型作为泛型参数,可随时释放内存。
SoIdesignedthisUnmangedArray
:no'unsafe'keyword,takesanystructasgenericparameter,canbereleasedanytimeyouwant. 1///2///元素类型为sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct的非托管数组。 3/// 5///不能使用enum类型作为T。 4///sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct,不能使用enum类型作为T。 6publicclassUnmanagedArray :UnmanagedArrayBasewhereT:struct 7{ 8 9/// 10///元素类型为sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct的非托管数组。 11/// 12///13[MethodImpl(MethodImplOptions.Synchronized)] 14publicUnmanagedArray(intcount) 15:base(count,Marshal.SizeOf(typeof(T))) 16{ 17} 18 19/// 20///获取或设置索引为 22///的元素。 21/// 23/// 24publicTthis[intindex] 25{ 26get 27{ 28if(index<0||index>=this.Count) 29thrownewIndexOutOfRangeException("indexofUnmanagedArrayisoutofrange"); 30 31varpItem=this.Header+(index*elementSize); 32//varobj=Marshal.PtrToStructure(pItem,typeof(T)); 33//Tresult=(T)obj; 34Tresult=Marshal.PtrToStructure (pItem);//worksin.net4.5.1 35returnresult; 36} 37set 38{ 39if(index<0||index>=this.Count) 40thrownewIndexOutOfRangeException("indexofUnmanagedArrayisoutofrange"); 41 42varpItem=this.Header+(index*elementSize); 43//Marshal.StructureToPtr(value,pItem,true); 44Marshal.StructureToPtr (value,pItem,true);//worksin.net4.5.1 45} 46} 47 48/// 49///按索引顺序依次获取各个元素。 50/// 51///52publicIEnumerable GetElements() 53{ 54if(!this.disposed) 55{ 56for(inti=0;i 65///非托管数组的基类。 66/// 67publicabstractclassUnmanagedArrayBase:IDisposable 68{ 69 70/// 71///数组指针。 72/// 73publicIntPtrHeader{get;privateset;} 74 75///76///元素数目。 77/// 78publicintCount{get;privateset;} 79 80///81///单个元素的字节数。 82/// 83protectedintelementSize; 84 85///86///申请到的字节数。(元素数目*单个元素的字节数)。 87/// 88publicintByteLength 89{ 90get{returnthis.Count*this.elementSize;} 91} 92 93 94///95///非托管数组。 96/// 97///元素数目。 98/// 单个元素的字节数。 99[MethodImpl(MethodImplOptions.Synchronized)] 100protectedUnmanagedArrayBase(intelementCount,intelementSize) 101{ 102this.Count=elementCount; 103this.elementSize=elementSize; 104 105intmemSize=elementCount*elementSize; 106this.Header=Marshal.AllocHGlobal(memSize); 107 108allocatedArrays.Add(this); 109} 110 111privatestaticreadonlyList allocatedArrays=newList (); 112 113/// 114///立即释放所有 116[MethodImpl(MethodImplOptions.Synchronized)] 117publicstaticvoidFreeAll() 118{ 119foreach(variteminallocatedArrays) 120{ 121item.Dispose(); 122} 123allocatedArrays.Clear(); 124} 125 126~UnmanagedArrayBase() 127{ 128Dispose(); 129} 130 131#regionIDisposableMembers 132 133///。 115/// 134///InternalvariablewhichchecksifDisposehasalreadybeencalled 135/// 136protectedBooleandisposed; 137 138///139///Releasesunmanagedand-optionally-managedresources 140/// 141///true toreleasebothmanagedandunmanagedresources;false toreleaseonlyunmanagedresources. 142protectedvoidDispose(Booleandisposing) 143{ 144if(disposed) 145{ 146return; 147} 148 149if(disposing) 150{ 151//Managedcleanupcodehere,whilemanagedrefsstillvalid 152} 153//Unmanagedcleanupcodehere 154IntPtrptr=this.Header; 155 156if(ptr!=IntPtr.Zero) 157{ 158this.Count=0; 159this.Header=IntPtr.Zero; 160Marshal.FreeHGlobal(ptr); 161} 162 163disposed=true; 164} 165 166///167///Performsapplication-definedtasksassociatedwithfreeing,releasing,orresettingunmanagedresources. 168/// 169publicvoidDispose() 170{ 171this.Dispose(true); 172GC.SuppressFinalize(this); 173} 174 175#endregion 176 177} UnmanagedArray如何使用(Howtouse)
UnmanagedArray
使用方式十分简单,就像一个普通的数组一样: UsingUnamangedAray
isjustlikeanormalarray(int[],vec3[],etc.): internalstaticvoidTypicalScene() { constintcount=100; //测试float类型 varfloatArray=newUnmanagedArray(count); for(inti=0;i (count); for(inti=0;i (count); for(inti=0;i (count); for(inti=0;i 快速读写UnmanagedArray
UnmanagedArrayHelper
由于很多时候需要申请和使用很大的UnmanagedArray
,直接使用this[index]索引方式速度会偏慢,所以我添加了几个辅助方法,专门解决快速读写UnmanagedArray 的问题。 publicstaticclassUnmanagedArrayHelper { //////////错误1无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针 ///// ////////// ///// //publicstaticunsafeT*FirstElement (thisUnmanagedArray array)whereT:struct //{ //varheader=(void*)array.Header; //return(T*)header; //} /// ///获取非托管数组的第一个元素的地址。 /// ////// publicstaticunsafevoid*FirstElement(thisUnmanagedArrayBasearray) { varheader=(void*)array.Header; returnheader; } publicstaticunsafevoid*LastElement(thisUnmanagedArrayBasearray) { varlast=(void*)(array.Header+(array.ByteLength-array.ByteLength/array.Length)); returnlast; } /// ///获取非托管数组的最后一个元素的地址再向后一个单位的地址。 /// ////// publicstaticunsafevoid*TailAddress(thisUnmanagedArrayBasearray) { vartail=(void*)(array.Header+array.ByteLength); returntail; } } 如何使用
这个类型实现了3个扩展方法,可以获取UnmanagedArray
的第一个元素的位置、最后一个元素的位置、最后一个元素+1的位置。用这种unsafe的方法可以实现C语言一样的读写速度。 下面是一个例子。用unsafe的方式读写UnmanagedArray
,速度比this[index]方式快10到70倍。 publicstaticvoidTypicalScene() { intlength=1000000; UnmanagedArrayarray=newUnmanagedArray (length); UnmanagedArray array2=newUnmanagedArray (length); longtick=DateTime.Now.Ticks; for(inti=0;i unsafe { vec3*header=(vec3*)vec3Array.FirstElement(); vec3*last=(vec3*)vec3Array.LastElement(); vec3*tailAddress=(vec3*)vec3Array.TailAddress(); inti=0; for(vec3*ptr=header;ptr<=last/*or:ptr2015-08-25
用StructLayout和MarshalAs支持复杂的struct
在OpenGL中我需要用UnmanagedArray
,其中mat4定义如下: 1///2///Representsa4x4matrix. 3/// 4[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4*4)] 5publicstructmat4 6{ 7///8///Getsorsetsthe 10///columnatthespecifiedindex. 9/// 11///The 13///column. 12/// Thecolumnindex. 14/// Thecolumnatindex 15publicvec4this[intcolumn] 16{ 17get{returncols[column];} 18set{cols[column]=value;} 19} 20 21///. 22///Getsorsetstheelementat 24///and . 23/// 25///Theelementat 27///and . 26/// Thecolumnindex. 28/// Therowindex. 29/// 30///Theelementat 32publicfloatthis[intcolumn,introw] 33{ 34get{returncols[column][row];} 35set{cols[column][row]=value;} 36} 37 38///and . 31/// 39///Thecolummsofthematrix. 40/// 41[MarshalAs(UnmanagedType.ByValArray,SizeConst=4)] 42privatevec4[]cols; 43} 44 45///46///Representsafourdimensionalvector. 47/// 48[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4)] 49publicstructvec4 50{ 51publicfloatx; 52publicfloaty; 53publicfloatz; 54publicfloatw; 55 56publicfloatthis[intindex] 57{ 58get 59{ 60if(index==0)returnx; 61elseif(index==1)returny; 62elseif(index==2)returnz; 63elseif(index==3)returnw; 64elsethrownewException("Outofrange."); 65} 66set 67{ 68if(index==0)x=value; 69elseif(index==1)y=value; 70elseif(index==2)z=value; 71elseif(index==3)w=value; 72elsethrownewException("Outofrange."); 73} 74} 75} mat4注意:UnmanagedArray
支持的struct,T的大小必须是确定的。所以在mat4里我们用[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4*4)]指定mat4的大小为4个vec4*4个float*4个字节(每个float)=64字节,并且在privatevec4[]cols;上用[MarshalAs(UnmanagedType.ByValArray,SizeConst=4)]规定了cols的元素数必须是4。之后在vec4上的[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4)]不写也可以,因为vec4只有4个简单的float字段,不含复杂类型。 下面是测试用例。
mat4matrix=glm.scale(mat4.identity(),newvec3(2,3,4)); varsize=Marshal.SizeOf(typeof(mat4)); size=Marshal.SizeOf(matrix); UnmanagedArrayarray=newUnmanagedArray (1); array[0]=matrix; mat4newMatirx=array[0];//newMatrixshouldbeequaltomatrix array.Dispose(); 如果matrix和newMatrix相等,就说明上述Attribute配置正确了。
总结
到此这篇关于C#+无unsafe的非托管大数组(largeunmanagedarrayinc#without'unsafe'keyword)的文章就介绍到这了,更多相关C#+无unsafe的非托管大数组内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!