程序中有多个线程并发执行,每个线程中都会记录日志文件(以天为单位生成文件),写文件非常频繁。20分钟内有3000多个线程。
这样导致在写文件的过程中发生I/o error 32 错误。有什么好的解决方案。
这样导致在写文件的过程中发生I/o error 32 错误。有什么好的解决方案。
解决方案 »
- delphi Socket Error #10061 connection refused
- ScrollBox 里放一个 panel 鼠标滚轮消息被panel截了,如何在发给ScrollBox
- 怎么刚才www.csdn.net首页被黑了吗?无法使用404错误呢。
- 600分求解一个不知数据格试的图形显示问题
- 要做一个定时转换数据的接口,请教怎样控制时间使其做到自动定时转换
- frpreview 加载不上fastreport报表,求助
- 在线等待,请求帮忙!
- DELPHI中的BUG
- 用了CorbaConnection的程序需要什么运行环境?
- 问一个简单的控件问题
- TGUID = '{CD2685F2-486F-4D43-98BF-5066FDD88799}'这个ID能改变吗?
- DELPHI编写一个能处理表达式的计算器!
(******************************************************************************)
(* 模 块 名: HSLogger4D.Pas *)
(* 别 名: 多任务线程安全日志接口-进程独立版 *)
(* 作 者: Unsigned(僵哥) *)
(* 说 明: 基于线程安全和文件缓存的单例多线程同步日志记录接口,本接口由一个 *)
(* Delphi版本的动态链接库来提供相应的功能,采用Delphi的 String 类型 *)
(* 传递参数,使用时请注意调用方式与Delphi一致,不建议使用于 Delphi *)
(* 以外的语言环境,由此所引发的后果,作者不承担任何责任 *)
(* 附带文件: HSLogger4D.DLL *)
(* 版 本 号: 1.1 *)
(* 创 建 于: 2007-07 *)
(* 备 注: 本接口属进程独立版不处理多进程间同步 *)
(* *)
(* 修改记录: 2007-07-13 创建最初版本 *)
(* 2007-07-14 修正多线程内存管理冲突 *)
(******************************************************************************)unit HSLogger4D;interface
uses
Windows;
(*********************************)
(* 功 能:记录日志 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:日志内容,自行控制换行符 *)
(* 参数3:记录日志后是否关闭文件 *)
(* (不会释放其它资源) *)
(* 返回值:0=成功,非0=LastError *)
(*********************************)
function WriteLogThreadsafe(
FileName : String;
LogString : String;
CloseFileAfterWrote : BOOL
) : DWORD;overload;function WriteLogThreadsafe(
FileName : String;
LogString : String
) : DWORD;overload;
(*********************************)
(* 功 能:关闭日志文件 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:记录日志文件关闭后是否 *)
(* 释放其它相关资源 *)
(*********************************)
procedure CloseFile(
FileName : String;
FreeAfterClose : BOOL = true
);(*********************************)
(* 功 能:关闭所有日志文件 *)
(* 参数1:记录日志文件关闭后是否 *)
(* 释放其它相关资源 *)
(*********************************)
procedure CloseAllFile(
FreeAfterClose : BOOL = false
);(*********************************)
(* 功 能:强制缓存写入文件 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:写入文件后是否关闭文件 *)
(* (不会释放其它资源) *)
(* 返回值:0=失败,非0=成功 *)
(*********************************)
function FlushFileToDisk(
FileName : String;
CloseFileAfterFlush : BOOL = true
) : BOOL;(*********************************)
(* 功 能:强制缓存写入文件 *)
(* 参数1:写入文件后是否关闭文件 *)
(* (不会释放其它资源) *)
(*********************************)
procedure FlushAllFileToDisk(
CloseFileAfterFlush : BOOL = false
);var
CloseFileAfterWrote:BOOL=false;implementation(******************************************************************************)
(* *)
(* <<日志接口定义>> *)
(* 注:请保持与源接口一致 *)
(******************************************************************************)
type
(*Logger Interface*)
ILogger=interface
['{E43E419D-26AD-48e6-8097-19622CC2043E}']
function WriteLog(
FileName : String;
LogString : String;
CloseFileAfterWrote : BOOL = true
) : DWORD; procedure CloseFile(
FileName : String;
FreeAfterClose : BOOL = true
); procedure CloseAllFile(
FreeAfterClose : BOOL = true
); function FlushFileToDisk(
FileName : String;
CloseFileAfterFlush : BOOL = true
) : BOOL; procedure FlushAllFileToDisk(
CloseFileAfterFlush : BOOL = true
);
end; (*Logger Import statement*)
function HSLoggerObject: ILogger; stdcall;
external 'HSLogger4D.dll'
name 'HSLoggerObject';(*Write Log-String to file*)
(*********************************)
(* 功 能:记录日志 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:日志内容,自行控制换行符 *)
(* 参数3:记录日志后是否关闭文件 *)
(* (不会释放其它资源) *)
(*********************************)
function WriteLogThreadsafe(
FileName : String;
LogString : String;
CloseFileAfterWrote : BOOL
) : DWORD;
begin
Result:=HSLoggerObject.WriteLog(
FileName,
LogString,
CloseFileAfterWrote
);
end;function WriteLogThreadsafe(
FileName : String;
LogString : String
) : DWORD;
begin
Result:=WriteLogThreadsafe(
FileName,
LogString,
CloseFileAfterWrote
);
end;(*Close one Logfile,and free logfile-Object if FreeAfterClose was set to TRUE*)
(*********************************)
(* 功 能:关闭日志文件 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:记录日志文件关闭后是否 *)
(*********************************)
procedure CloseFile(
FileName : String;
FreeAfterClose : BOOL);
begin
HSLoggerObject.CloseFile(
FileName,
FreeAfterClose
);
end;(*Close All Logfile,and free logfile-Object if FreeAfterClose was set to TRUE*)
(*********************************)
(* 功 能:关闭所有日志文件 *)
(* 参数1:记录日志文件关闭后是否 *)
(*********************************)
procedure CloseAllFile( FreeAfterClose : BOOL = false );
begin
HSLoggerObject.CloseAllFile(FreeAfterClose);
end;(*********************************)
(* 功 能:强制缓存写入文件 *)
(* 参数1:日志文件名,支持相对路径 *)
(* 参数2:写入文件后是否关闭文件 *)(* 返回值:0=失败,非0=成功 *)
(*********************************)
function FlushFileToDisk( FileName : String;
CloseFileAfterFlush : BOOL
) : BOOL;
begin
Result:=HSLoggerObject.FlushFileToDisk(
FileName,
CloseFileAfterFlush
);
end;(*********************************)
(* 功 能:强制缓存写入文件 *)
(* 参数1:写入文件后是否关闭文件 *)
(*********************************)
procedure FlushAllFileToDisk( CloseFileAfterFlush : BOOL );
begin
HSLoggerObject.FlushAllFileToDisk(CloseFileAfterFlush);
end;
end.
http://download.csdn.net/source/332395
(*TFileLoggerObject*)
TFileLoggerObject=class(TObject)
private
FFileName:String; //文件名
FFileHandle:THandle; //文件句柄
{$ifdef _MT}
FFileLock:TRTLCriticalSection; //文件操作开关
{$endif}
protected
procedure InternalCloseFile; //关闭文件
function WriteLog(LogMsg:String):Integer; //日志信息入库 **写入文件** protected
function TryLockFile:BOOL; //文件开关尝试入口
procedure LockFile; //文件开关入口
procedure UnlockFile; //文件开关出口 public
(*构造函数*)
constructor Create( LogFileName:String //文件名,必须
);
(*析构函数*)
Destructor Destroy;override; public (*<<属性>>*)
(* <<只读>> 文件名 *)
property FileName:String read FFileName; (* <<只读>> 文件句柄 *)
property FileHandle:THandle read FFileHandle; public (* 无队列日志写入,通过对比较重要的信息采用此接口 *)
procedure PushLog(LogMsg:String;var ErrorCode:DWORD;bCloseFile:BOOL=false); (* 关闭日志文件 *)
procedure CloseFile; (* 强制文件缓存写入物理文件 *)
function FlushFileToDisk(CloseFileAfterFlush:BOOL=true):BOOL; end;
(******************************************************************************)
(* <<日志文件类实现>> *)
(******************************************************************************)
(*TFileLoggerObject*)procedure TFileLoggerObject.PushLog(LogMsg: string; var ErrorCode: DWORD;bCloseFile:BOOL);
begin
LockFile;
try
ErrorCode:=WriteLog(LogMsg);
if bCloseFile then
begin
InternalCloseFile;
end;
finally
UnlockFile;
end;
end;procedure TFileLoggerObject.CloseFile;
begin
LockFile;
try
InternalCloseFile;
finally
UnlockFile;
end;
end;Function TFileLoggerObject.FlushFileToDisk(CloseFileAfterFlush: BOOL):BOOL;
begin
Result:=false;
LockFile;
try
if FFileHandle=INVALID_HANDLE_VALUE then
begin
Exit;
end;
Result:=FlushFileBuffers(FFileHandle);
if CloseFileAfterFlush then
InternalCloseFile;
finally
UnlockFile;
end;
end;procedure TFileLoggerObject.InternalCloseFile;
begin
if (FFileHandle<>INVALID_HANDLE_VALUE) then
begin
CloseHandle(FFileHandle);
FFileHandle:=INVALID_HANDLE_VALUE;
end;
end;function TFileLoggerObject.WriteLog(LogMsg: string):Integer;
var
WroteSize:DWORD;
BytesToWrite:DWORD;
Buffer:Pointer;
begin
BytesToWrite:=Length(LogMsg);
Result:=0;
if BytesToWrite<=0 then
begin
Exit;
end;
if self.FFileHandle=INVALID_HANDLE_VALUE then
begin
try
if Not CheckDir(ExtractFilePath(FileName)) then
begin
Result := GetLastError;
Exit;
end;
FFileHandle:=CreateFile(@FileName[1],
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ,
nil,
OPEN_ALWAYS,//CREATE_NEW
FILE_ATTRIBUTE_NORMAL,
0);
if FFileHandle=INVALID_HANDLE_VALUE then
begin
Result:=GetLastError;
Exit;
end;
if GetLastError=ERROR_ALREADY_EXISTS then
begin
if SetFilePointer(FFileHandle,0,nil,FILE_END)=INVALID_SET_FILE_POINTER then
begin
Result:=GetLastError;
end
else
SetLastError(0);
end;
except
Result:=GetLastError;
Exit;
end;
end;
Buffer:=Pointer(LogMsg);
while BytesToWrite>0 do
begin
WroteSize:=0;
if Not WriteFile(FFileHandle,Buffer^,BytesToWrite,WroteSize,nil) then
begin
Result:=GetLastError;
Exit;
end;
Buffer:=Pointer(DWORD(Buffer)+WroteSize);
Dec(BytesToWrite,WroteSize);
end;end;procedure TFileLoggerObject.LockFile;
begin
{$ifdef _MT}
EnterCriticalSection(FFileLock);
{$endif}
end;procedure TFileLoggerObject.UnlockFile;
begin
{$ifdef _MT}
LeaveCriticalSection(FFileLock);
{$endif}
end;function TFileLoggerObject.TryLockFile:BOOL;
begin
{$ifdef _MT}
Result:=TryEnterCriticalSection(FFileLock);
{$else}
Result:=True;
{$endif}
end;constructor TFileLoggerObject.Create(LogFileName: string);
begin
FFileName:=LogFileName;
FFileHandle:=INVALID_HANDLE_VALUE;
{$ifdef _MT}
InitializeCriticalSection(FFileLock);
{$endif}
end;destructor TFileLoggerObject.Destroy;
begin
LockFile;
try
if FFileHandle<>INVALID_HANDLE_VALUE then
CloseHandle(FFileHandle);
finally
UnlockFile;
{$ifdef _MT}
DeleteCriticalSection(FFileLock);
{$endif}
end;
Inherited;
end;
except
Result:=GetLastError;
Exit;
end;
...这里的Result永远都为0.如果实际上在这个时候,单纯造返回错误号,已经没太多的意义,这应该是一个未知的错误,建议性改用-1.
SetFilePointer(FFileHandle,0,nil,FILE_END)=INVALID_SET_FILE_POINTER
这个是要引用Delphi的哪个系统单元啊。
并发写文件如此并发,还是用Pooling技术吧
自己实现线程池来处理
或考虑减少并发
重要的是俺认为你的设计存在结构错误,如此多线程的文件操作,最好避免
SetFilePointer(FFileHandle,0,nil,FILE_END)=INVALID_SET_FILE_POINTER
这个是要引用Delphi的哪个系统单元啊。
==================
SetFilePointer是一个系统API,引用Windows单元就可以。
CheckDir是自己写的一个函数
function CheckDir(const Dir:String):Boolean;
var
DirList:TStringList;
SubDir:String;
I: Integer;
begin
Result := True;
SubDir:=Dir;
if DirectoryExists(SubDir) then
Exit; DirList:=TStringList.Create;
try
repeat
while SubDir[Length(SubDir)] ='\' do SubDir := Copy(SubDir,1,Length(SubDir)-1);
if Length(SubDir)=0 then
break;
DirList.Add(SubDir);
SubDir := ExtractFilePath(SubDir);
if DirectoryExists(SubDir) then break;
until true;
for I := DirList.Count downto 1 do
begin
SubDir := DirList.Strings[i-1];
if Not CreateDirectory(@SubDir[1],nil) then
begin
Result := false;
Exit;
end;
end;
finally
DirList.Free;
end;
end;其实楼主说的仅仅只是记录日志,不是比较严谨的文件操作,没有太多必要进行内存池操作,当然如果实现了完成端口去操作,反倒比较推荐去使用。另外楼主说的20分钟有三千个线程操作文件,这个得看具体的情况。我所采用的是一种单例模式管理。相对操作同一个文件来讲,多个线程确实存在着排队和撞车事件。使用队列虽然可以让需要记日志的线程独立出来,但是却增大了内存的开销。并且对同一文件的操作同一时间也只能是一个线程进行操作。因为谁也无法预期到一次可能会存多少内容,或者说一次写文件操作并不一定能完成,如果就有可能导致文件操作过程当中的数据混乱。其实在最初的设想当中,我也想过实现日志队列,但是这个就需要库当中进行线程管理。而线程管理,特别是作为一个类似插件来为别人做一个独立的线程管理,这是不推荐的,因为任何可能的问题都会出现,并且双方都不可以预知,从而加入了线程会远远增大复杂度,而我的目标仅仅只是一条条完整地记录日志数据,在高并发服务当中,记录日志也仅仅只是其中的一小小部分。当然,日志也有多种多样的类型,还有一些日志是非常重要的,甚至还需要防篡改,和复杂查询(可能需要入库)等等,那时候日志就是一整个项目当中致关重要的工作了。个人觉得,这应该不算是这贴子当中讨论的重点,或者至少楼主并没有给出更加明确的信息提示需要如此去探讨,毕竟那牵涉的内容太宽了。