Delphi的FIFO实现
FIFO主要用于多个不同线程或进程之间数据交换时做缓冲区用,尤其适合实时数据通讯应用中的数据缓冲,接收线程(进程)将数据写入FIFO,处理线程(进程)从FIFO取出数据
本单元中:
TMemoryFIFO类适用于单进程内不同线程之间交换数据
TMapFileFIFO类适用于不同进程之间交换数据
Unit UtFIFO; Interface Uses Windows, SysUtils, SyncObjs; Type PFIFOStruct= ^TFIFOStruct; TFIFOStruct= Record FSize: Integer; FWritePtr: Integer; FReadPtr: Integer; FBuffer: TByteArray; End; TFIFOReadFunc= Function(Buf: Pointer; Count: Integer): Integer; TFIFOReadFuncOfObject= Function(const Buf; Count: Integer): Integer Of Object; TAbstractFIFO= Class Protected FSelfAccess: Boolean; FDataStruct: PFIFOStruct; // 数据区指针 Procedure AllocateResource(Size: Integer); Virtual; Abstract; Procedure FreeResources; Virtual; Abstract; Procedure Lock; Virtual; Abstract; Procedure UnLock; Virtual; Abstract; Public Function FIFOFreeSpace: Integer; Function FIFOUsedSpace: Integer; Function CheckFIFOFull: Boolean; Function CheckFIFOEmpty: Boolean; Function WriteData(const Buf: Pointer; Count: Integer): Integer; Virtual; Function ReadData(Buf: Pointer; Count: Integer): Integer; Virtual; Function ReadDataByFunc(Func: TFIFOReadFuncOfObject; Count: Integer): Integer; Virtual; Constructor Create(Size: Integer); Virtual; Destructor Destroy; Override; Procedure Empty; Function Size: Integer; End; TMemoryFIFO= Class(TAbstractFIFO) Protected FLocker: TCriticalSection; Procedure AllocateResource(Size: Integer); Override; Procedure FreeResources; Override; Procedure Lock; Override; Procedure UnLock; Override; Public Constructor Create(Size: Integer); Override; Destructor Destroy; Override; End; TFileMapFIFO= Class(TAbstractFIFO) Private FMaster:Boolean; FMapHandle: THandle; // 内存映射文件句柄 FMutexHandle: THandle; // 互斥句柄 FMapName: String; // 内存映射对象 FPVHandle: THandle; Protected Procedure AllocateResource(Size: Integer); Override; Procedure FreeResources; Override; Procedure Lock; Override; Procedure UnLock; Override; Public Constructor Create(Const MapName: String; Size: Integer;bMaster:Boolean); Overload; Destructor Destroy; Override; Function WriteData(const Buf: Pointer; Count: Integer): Integer; Override; Function ReadData(Buf: Pointer; Count: Integer): Integer; Override; property PVHandle:NativeUInt read FPVHandle; End; Implementation Function Min(Const A, B: Integer): Integer; inline; begin if A>B then Result:=B else Result:=A end; Constructor TAbstractFIFO.Create(Size: Integer); Begin Inherited Create; AllocateResource(Size); If Not Assigned(FDataStruct) Then Raise Exception.Create('FIFO申请内存失败'); End; Destructor TAbstractFIFO.Destroy; Begin FreeResources; Inherited; End; Function TAbstractFIFO.FIFOFreeSpace; Begin With FDataStruct^ Do Begin Lock; If FWritePtr> FReadPtr Then Result:= (FSize- FWritePtr)+ FReadPtr- 1 Else If FWritePtr< FReadPtr Then Result:= FReadPtr- FWritePtr- 1 Else Result:= FSize; UnLock; End; End; Function TAbstractFIFO.FIFOUsedSpace; Begin With FDataStruct^ Do Begin Lock; If FWritePtr> FReadPtr Then Result:= FWritePtr- FReadPtr Else If FWritePtr< FReadPtr Then Result:= (FSize- FReadPtr)+ FWritePtr Else Result:= 0; UnLock; End; End; Function TAbstractFIFO.CheckFIFOFull: Boolean; Begin With FDataStruct^ Do Begin Lock; If (FWritePtr= FSize- 1)And (FReadPtr= 0) Then Result:= True Else If (FWritePtr+ 1= FReadPtr) Then Result:= True Else Result:= False; UnLock; End; End; Function TAbstractFIFO.CheckFIFOEmpty: Boolean; Begin With FDataStruct^ Do Begin Lock; Result:= (FWritePtr= FReadPtr); UnLock; End; End; Function TAbstractFIFO.WriteData(const Buf: Pointer; Count: Integer): Integer; Var N: Integer; Begin Result:= 0; If Count<= 0 Then Exit; With FDataStruct^ Do Begin Lock; If FWritePtr< FReadPtr Then //如果没有满或已满 Begin Result:= Min(Count, FReadPtr- FWritePtr- 1); Move(Buf^, FBuffer[FWritePtr], Result); FWritePtr:= (FWritePtr+ Result)Mod FSize; End Else If FWritePtr = FReadPtr Then //Buffer 空 Begin Result:= Min(Count, FSize- 1); Move(Buf^, FBuffer[0], Result); FWritePtr:= Result; FReadPtr:= 0; End Else Begin Result:= Min(Count, FSize- FWritePtr); Move(Buf^, FBuffer[FWritePtr], Result); if Result=Count then FWritePtr:= (FWritePtr+ Result) Mod FSize else Begin N:= Min(Count- Result, FReadPtr); Move(PByteArray(Buf)^[Result], FBuffer[0], N); FWritePtr:= N; Result:= Result+ N; End; End; UnLock; End; End; Function TAbstractFIFO.ReadData(Buf: Pointer; Count: Integer): Integer; Var N: Integer; Begin Result:= 0; If Count<= 0 Then Exit; With FDataStruct^ Do Begin Lock; If FReadPtr< FWritePtr Then Begin Result:= Min(Count, FWritePtr- FReadPtr); Move(FBuffer[FReadPtr], Buf^, Result); FReadPtr:= (FReadPtr+ Result)Mod FSize; End Else if FReadPtr>FWritePtr Then Begin Result:= Min(Count, FSize- FReadPtr); Move(FBuffer[FReadPtr], Buf^, Result); if Result=Count then FReadPtr:=(FReadPtr+Result) mod FSize else Begin N:= Min(Count- Result, FWritePtr); Move(FBuffer[0], PByteArray(Buf)[Result], N); FReadPtr:= N; Result:= Result+ N; End; End; UnLock; End; End; Function TAbstractFIFO.ReadDataByFunc(Func: TFIFOReadFuncOfObject; Count: Integer): Integer; Var N, M: Integer; Begin Result:= 0; If Count<= 0 Then Exit; With FDataStruct^ Do Begin Lock; Try If FReadPtr< FWritePtr Then Begin Result:= Func(FBuffer[FReadPtr], Min(Count, FWritePtr- FReadPtr)); FReadPtr:= (FReadPtr+ Result)Mod FSize; End Else if FReadPtr>FWritePtr Then Begin Result:= Func(FBuffer[FReadPtr], Min(Count, FSize- FReadPtr)); if Result=Count then FReadPtr:=(FReadPtr+Result) mod FSize else Begin N:= Func(FBuffer[0], Min(Count- Result, FWritePtr)); FReadPtr:= N; Result:= Result+ N; End; End; Finally UnLock; End; End; End; Procedure TAbstractFIFO.Empty; Begin Lock; With FDataStruct^ Do Begin FWritePtr:= 0; FReadPtr:= 0; End; UnLock; End; Function TAbstractFIFO.Size: Integer; Begin Result:= FDataStruct^.FSize- 1; End; Constructor TMemoryFIFO.Create(Size: Integer); Begin Inherited Create(Size); FLocker:= TCriticalSection.Create; End; Destructor TMemoryFIFO.Destroy; Begin FLocker.Free; Inherited; End; Procedure TMemoryFIFO.AllocateResource(Size: Integer); Begin Inherited; GetMem(FDataStruct, Size+ 3* Sizeof(Integer)); With FDataStruct^ Do Begin FSize:= Size; FWritePtr:= 0; FReadPtr:= 0; End; End; Procedure TMemoryFIFO.FreeResources; Begin FreeMem(FDataStruct, FDataStruct^.FSize+ 3* Sizeof(Integer)); Inherited; End; Procedure TMemoryFIFO.Lock; Begin FLocker.Enter; End; Procedure TMemoryFIFO.UnLock; Begin FLocker.Leave; End; // 构造函数 Constructor TFileMapFIFO.Create(Const MapName: String; Size: Integer;bMaster:Boolean); Begin FMapName:= MapName; FMaster:=bMaster; Inherited Create(Size); End; Destructor TFileMapFIFO.Destroy; Begin CloseHandle(FPVHandle); Inherited; End; Procedure TFileMapFIFO.AllocateResource(Size: Integer); Begin Inherited; if FMaster then begin FMapHandle:= CreateFileMapping($FFFFFFFF, Nil, PAGE_READWRITE, 0, Size+ 3* Sizeof(Integer), PChar(FMapName)); If (FMapHandle= INVALID_HANDLE_VALUE)Or (FMapHandle= 0) Then Raise Exception.Create('创建文件映射对象失败!'); end else FMapHandle:=OpenFileMapping(FILE_MAP_ALL_ACCESS,False,PChar(FMapName)); FDataStruct:= MapViewOfFile(FMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); // 创建互斥对象,在写文件映射空间时用到它,以保持数据同步 FMutexHandle:= Windows.CreateMutex(Nil, False, PChar(FMapName+ '.Mtx')); FPVHandle := CreateEvent(nil,True,False,PChar(FMapName + '.PV')); If (FMutexHandle= 0)or(FPVHandle = 0) Then Raise Exception.Create('创建互斥对象失败'); // 判断是否已经建立文件映射了 If (FMapHandle <> 0)And (GetLastError = ERROR_ALREADY_EXISTS) Then Begin End Else Begin FillChar(FDataStruct^, Size+ 3* Sizeof(Integer), 0); FDataStruct^.FSize:= Size; End End; Procedure TFileMapFIFO.FreeResources; Begin UnmapViewOfFile(FDataStruct); CloseHandle(FMutexHandle); CloseHandle(FMapHandle); Inherited; End; Procedure TFileMapFIFO.Lock; Begin WaitForSingleObject(FMutexHandle, INFINITE); // =WAIT_OBJECT_0) End; Procedure TFileMapFIFO.UnLock; Begin ReleaseMutex(FMutexHandle); End; Function TFileMapFIFO.WriteData(const Buf: Pointer; Count: Integer): Integer; Begin Lock; Result:= Inherited WriteData(Buf, Count); SetEvent(FPVHandle); UnLock; End; Function TFileMapFIFO.ReadData(Buf: Pointer; Count: Integer): Integer; Begin Lock; Result:= Inherited ReadData(Buf, Count); UnLock; End; End.