Delphi多线程文件写入:I/O错误32

2024-02-09

我创建了一个类,用于在文本文件中写入线程安全日志,使用CriticalSection.

我不是 CriticalSection 和多线程编程(...和 ​​Delphi)的专家,我肯定做错了什么...

unit ErrorLog;

interface

uses
  Winapi.Windows, System.SysUtils;

type
    TErrorLog = class
    private
      FTextFile : TextFile;
      FLock     : TRTLCriticalSection;
    public
      constructor Create(const aLogFilename:string);
      destructor  Destroy; override;
      procedure   Write(const ErrorText: string);
    end;

implementation


constructor TErrorLog.Create(const aLogFilename:string);
begin
  inherited Create;

  InitializeCriticalSection(FLock);

  AssignFile(FTextFile, aLogFilename);

  if FileExists(aLogFilename) then
    Append(FTextFile)
  else
    Rewrite(FTextFile);
end;


destructor TErrorLog.Destroy;
const
    fmTextOpenWrite = 55218;
begin
    EnterCriticalSection(FLock);
    try
      if TTextRec(FTextFile).Mode <> fmTextOpenWrite then
        CloseFile(FTextFile);

      inherited Destroy;
    finally
      LeaveCriticalSection(FLock);
      DeleteCriticalSection(FLock);
    end;
end;


procedure TErrorLog.Write(const ErrorText: string);
begin
  EnterCriticalSection(FLock);

  try
    WriteLn(FTextFile, ErrorText);
  finally
    LeaveCriticalSection(FLock);
  end;
end;

end.

为了测试该类,我创建了一个表单,并将计时器设置为 100 毫秒:

procedure TForm1.Timer1Timer(Sender: TObject);
var
  I : integer;
  aErrorLog : TErrorLog;
begin
  aErrorLog := nil;
  for I := 0 to 1000 do begin
    try
      aErrorLog := TErrorLog.Create(FormatDateTime('ddmmyyyy', Now) + '.txt');
      aErrorLog.Write('new line');
    finally
      if Assigned(aErrorLog) then FreeAndNil(aErrorLog);
    end;
  end;
end;

日志已写入,但偶尔会引发I/O Error 32例外CloseFile(FTextFile)(可能是因为在另一个线程中使用)

我哪里做错了?

UPDATE:

阅读所有评论和答案后,我完全改变了方法。我分享我的解决方案。

ThreadUtilities.pas

(* Implemented for Delphi3000.com Articles, 11/01/2004
        Chris Baldwin
        Director & Chief Architect
        Alive Technology Limited
        http://www.alivetechnology.com
*)
unit ThreadUtilities;

interface

uses Windows, SysUtils, Classes;

type
    EThreadStackFinalized = class(Exception);
    TSimpleThread = class;

    // Thread Safe Pointer Queue
    TThreadQueue = class
    private
        FFinalized: Boolean;
        FIOQueue: THandle;
    public
        constructor Create;
        destructor Destroy; override;
        procedure Finalize;
        procedure Push(Data: Pointer);
        function Pop(var Data: Pointer): Boolean;
        property Finalized: Boolean read FFinalized;
    end;

    TThreadExecuteEvent = procedure (Thread: TThread) of object;

    TSimpleThread = class(TThread)
    private
        FExecuteEvent: TThreadExecuteEvent;
    protected
        procedure Execute(); override;
    public
        constructor Create(CreateSuspended: Boolean; ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
    end;

    TThreadPoolEvent = procedure (Data: Pointer; AThread: TThread) of Object;

    TThreadPool = class(TObject)
    private
        FThreads: TList;
        FThreadQueue: TThreadQueue;
        FHandlePoolEvent: TThreadPoolEvent;
        procedure DoHandleThreadExecute(Thread: TThread);
    public
        constructor Create( HandlePoolEvent: TThreadPoolEvent; MaxThreads: Integer = 1); virtual;
        destructor Destroy; override;
        procedure Add(const Data: Pointer);
    end;

implementation

{ TThreadQueue }

constructor TThreadQueue.Create;
begin
    //-- Create IO Completion Queue
    FIOQueue := CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    FFinalized := False;
end;

destructor TThreadQueue.Destroy;
begin
    //-- Destroy Completion Queue
    if (FIOQueue <> 0) then
        CloseHandle(FIOQueue);
    inherited;
end;

procedure TThreadQueue.Finalize;
begin
    //-- Post a finialize pointer on to the queue
    PostQueuedCompletionStatus(FIOQueue, 0, 0, Pointer($FFFFFFFF));
    FFinalized := True;
end;

(* Pop will return false if the queue is completed *)
function TThreadQueue.Pop(var Data: Pointer): Boolean;
var
    A: Cardinal;
    OL: POverLapped;
begin
    Result := True;

    if (not FFinalized) then
    //-- Remove/Pop the first pointer from the queue or wait
        GetQueuedCompletionStatus(FIOQueue, A, ULONG_PTR(Data), OL, INFINITE);

    //-- Check if we have finalized the queue for completion
    if FFinalized or (OL = Pointer($FFFFFFFF)) then begin
        Data := nil;
        Result := False;
        Finalize;
    end;
end;

procedure TThreadQueue.Push(Data: Pointer);
begin
    if FFinalized then
        Raise EThreadStackFinalized.Create('Stack is finalized');
    //-- Add/Push a pointer on to the end of the queue
    PostQueuedCompletionStatus(FIOQueue, 0, Cardinal(Data), nil);
end;

{ TSimpleThread }

constructor TSimpleThread.Create(CreateSuspended: Boolean;
  ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
begin
    FreeOnTerminate := AFreeOnTerminate;
    FExecuteEvent := ExecuteEvent;
    inherited Create(CreateSuspended);
end;

procedure TSimpleThread.Execute;
begin
    if Assigned(FExecuteEvent) then
        FExecuteEvent(Self);
end;

{ TThreadPool }

procedure TThreadPool.Add(const Data: Pointer);
begin
    FThreadQueue.Push(Data);
end;

constructor TThreadPool.Create(HandlePoolEvent: TThreadPoolEvent;
  MaxThreads: Integer);
begin
    FHandlePoolEvent := HandlePoolEvent;
    FThreadQueue := TThreadQueue.Create;
    FThreads := TList.Create;
    while FThreads.Count < MaxThreads do
        FThreads.Add(TSimpleThread.Create(False, DoHandleThreadExecute, False));
end;

destructor TThreadPool.Destroy;
var
    t: Integer;
begin
    FThreadQueue.Finalize;
    for t := 0 to FThreads.Count-1 do
        TThread(FThreads[t]).Terminate;
    while (FThreads.Count > 0) do begin
        TThread(FThreads[0]).WaitFor;
        TThread(FThreads[0]).Free;
        FThreads.Delete(0);
    end;
    FThreadQueue.Free;
    FThreads.Free;
    inherited;
end;

procedure TThreadPool.DoHandleThreadExecute(Thread: TThread);
var
    Data: Pointer;
begin
    while FThreadQueue.Pop(Data) and (not TSimpleThread(Thread).Terminated) do begin
        try
            FHandlePoolEvent(Data, Thread);
        except
        end;
    end;
end;

end.

ThreadFileLog.pas

(* From: http://delphi.cjcsoft.net/viewthread.php?tid=45763 *)
unit ThreadFileLog;

interface

uses Windows, ThreadUtilities, System.Classes;

type
    PLogRequest = ^TLogRequest;
    TLogRequest = record
        LogText  : String;
        FileName : String;
    end;

    TThreadFileLog = class(TObject)
    private
        FThreadPool: TThreadPool;
        procedure HandleLogRequest(Data: Pointer; AThread: TThread);
    public
        constructor Create();
        destructor Destroy; override;
        procedure Log(const FileName, LogText: string);
    end;

implementation

uses
  System.SysUtils;

(* Simple reuse of a logtofile function for example *)
procedure LogToFile(const FileName, LogString: String);
var
    F: TextFile;
begin
    AssignFile(F, FileName);

    if not FileExists(FileName) then
        Rewrite(F)
    else
        Append(F);

    try
        Writeln(F, LogString);
    finally
        CloseFile(F);
    end;
end;

constructor TThreadFileLog.Create();
begin
    FThreadPool := TThreadPool.Create(HandleLogRequest, 1);
end;

destructor TThreadFileLog.Destroy;
begin
    FThreadPool.Free;
    inherited;
end;

procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread);
var
    Request: PLogRequest;
begin
    Request := Data;
    try
        LogToFile(Request^.FileName, Request^.LogText);
    finally
        Dispose(Request);
    end;
end;

procedure TThreadFileLog.Log(const FileName, LogText: string);
var
    Request: PLogRequest;
begin
    New(Request);
    Request^.LogText  := LogText;
    Request^.FileName := FileName;
    FThreadPool.Add(Request);
end;

end.

基本形式示例

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,
  Vcl.StdCtrls, ThreadFileLog;

type
  TForm1 = class(TForm)
    BtnStart: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BtnStartClick(Sender: TObject);
    private
    FThreadFileLog : TThreadFileLog;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.BtnStartClick(Sender: TObject);
var
I : integer;
aNow : TDateTime;
begin
    aNow := Now;

    for I := 0 to 500 do
       FThreadFileLog.Log(
        FormatDateTime('ddmmyyyyhhnn', aNow) + '.txt',
        FormatDateTime('dd-mm-yyyy hh:nn:ss.zzz', aNow) + ': I: ' + I.ToString
      );

    ShowMessage('logs are performed!');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    FThreadFileLog := TThreadFileLog.Create();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
    FThreadFileLog.Free;

    ReportMemoryLeaksOnShutdown := true;
end;




end.

输出日志:

30-11-2014 14.01.13.252: I: 0
30-11-2014 14.01.13.252: I: 1
30-11-2014 14.01.13.252: I: 2
30-11-2014 14.01.13.252: I: 3
30-11-2014 14.01.13.252: I: 4
30-11-2014 14.01.13.252: I: 5
30-11-2014 14.01.13.252: I: 6
30-11-2014 14.01.13.252: I: 7
30-11-2014 14.01.13.252: I: 8
30-11-2014 14.01.13.252: I: 9
...
30-11-2014 14.01.13.252: I: 500

而不是检查TTextRec(FTextFile).Mode <> fmTextOpenWrite您应该检查您的文件是否已关闭,如果是not关闭然后你关闭它。

尝试用以下代码替换提到的检查:

if TTextRec(FTextFile).Mode <> fmClosed then
  CloseFile(FTextFile);

Edited:

这与防病毒软件锁定文件无关。这只是析构函数中的一个简单错误。

文件已经以打开写入模式打开,原始代码仅在文件打开时关闭文件not在开放写入模式下 - 就是这样never关闭文件。

希望这能解释错误发生在哪里。

至于logger的类的整体设计。这不是问题,问题很简单,我提供了一个简单且可行的解决方案。

我认为如果 Simone 希望我们教他如何设计记录器类,那么他会提出要求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Delphi多线程文件写入:I/O错误32 的相关文章

  • 使用文本和进度条填充 DataGridView

    我正在创建一个多线程应用程序 其中每个线程将在我的应用程序中显示为一行DataGridView 我想要一个ProgressBar每行指示相应的线程进度 问题是 这可能吗 如果是这样 怎么办 我添加了类 DataGridView Progre
  • Objective C 中最好的多线程方法?

    我正在开发一个 iPad 应用程序 目前正在努力寻找多线程的最佳方法 让我用一个简化的例子来说明这一点 我有一个包含 2 个子视图的视图 一个目录选择器和一个包含所选目录中所有图像缩略图的图库 由于 下载 和生成这些缩略图可能需要相当长的时
  • 在 Linux 上创建线程与进程的开销

    我试图回答在 python 中创建线程与进程有多少开销的问题 我修改了类似问题的代码 该问题基本上运行一个带有两个线程的函数 然后运行带有两个进程的相同函数并报告时间 import time sys NUM RANGE 100000000
  • C# 锁(mylocker) 不起作用

    我有很多 Web 服务调用 异步 在回调中 我会将结果绘制到 Excel 中 我想同步绘图方法 所以我使用以下内容 但是 从我在 Visual Studio 中追踪到 每次 lock locker 都会成功 并且有许多线程运行clearco
  • C++:Linux平台上的线程同步场景

    我正在为 Linux 平台实现多线程 C 程序 其中我需要类似于 WaitForMultipleObjects 的功能 在搜索解决方案时 我发现有一些文章描述了如何在 Linux 中实现 WaitForMultipleObjects 功能
  • java中线程之间的通信:如果另一个线程完成则停止一个线程

    仅当另一个线程也在运行时 如何才能使一个线程运行 这意味着 如果我从一个线程中的运行返回 那么我希望另一个线程也停止运行 我的代码看起来像这样 ClientMessageHandler clientMessagehandler new Cl
  • Firebird 或 NexusDB

    我知道有很多与 Delphi 数据库相关的问题 但我只考虑这两个数据库 我需要查询大约 100 000 条记录 根据您的经验 哪个更快 作为嵌入式 as C S Thanks 我还没用过 Nexus tbh 但我经常使用 Firebird
  • Python GIL 防止多核机器中 CPU 使用率超过 100%?

    许多参考文献都说 Python GIL 降低了多核机器中多线程代码的性能 因为每个线程在执行之前都需要获取 GIL 换句话说 看起来GIL实际上是将多线程Python程序变成了单线程模式 例如 1 线程A获得GIL 执行一段时间 释放GIL
  • 是否可以在虚拟树视图中选择多个列?

    我需要添加功能来复制节点和列的矩形选择 但我找不到任何方法来实际选择虚拟树视图中的多个列 除了 toFullRowSelect 之外 我只是错过了什么吗 如果没有 是否有一个后代具有类似网格的多列选择支持 经过一些测试后 我得出了以下结论
  • java:为什么主线程等待子线程完成

    我有一个简单的java程序 主线程 main 创建并启动另一个线程t class T extends Thread Override public void run while true System out println Inside
  • BufferBlock 连续

    我想使用以下方式实现消费者 生产者模式BufferBlock
  • 让线程在窗体关闭时保持运行

    我在我的应用程序上创建了一个同步线程 我想知道如果我关闭申请表 是否有办法让该线程保持打开状态 直到完成同步过程 调用线程的WaitFor方法在您的 DPR 文件中 之后Application Run线 如果线程已经运行完毕 那么WaitF
  • 多个线程访问一个变量

    我在正在读的一本教科书中发现了这个问题 下面也给出了解决方案 我无法理解最小值怎么可能是 2 为什么一个线程不能读取 0 而所有其他线程都执行并写入 1 而无论是1还是2 最后写入的线程仍然必须完成自己的循环 int n 0 int mai
  • DBX 错误:驱动程序无法正确初始化

    我在跑步德尔福XE3 终极版 MySQL 数据库 这是我点击时收到的错误Test Connection 作为回应 我在 xampp 目录中找到了 libmysql 库 并将其复制到我的 System32 目录中 但这是行不通的 此消息指的是
  • 线程安全的有限大小队列,不使用锁

    我正在尝试编写一个主题队列 但遇到死锁和其他多线程问题 我想用Interlocked CompareExchange避免lock用法 但这段代码并没有按预期工作 它只是擦除整个队列 我在这里做错了什么 public class FixedS
  • nHibernate 使用 Log4Net 进行日志记录,线程会话问题

    大家好 这里有一个小问题 我正在努力解决这个问题 我目前正在开始使用 nHibernate 由于工作需要 我不得不这样做 并且我在 nHibernate 的会话和多线程方面遇到了一些困难 我想在这里完成的任务是让 Log4Net 将所有内容
  • java中的负载均衡线程池的种类

    我正在寻找一个负载平衡的线程池 到目前为止还没有成功 不确定负载平衡是否是正确的措辞 让我解释一下我试图实现的目标 第1部分 我有 Jobs 有 8 到 10 个单一任务 在 6 核 CPU 上 我让 8 个线程并行处理此任务 这似乎提供了
  • Delphi:如何计算大文件的 SHA 哈希值

    您好 我需要生成 5 Gig 文件的 SHA 您知道有一个非基于字符串的 Delphi 库可以做到这一点吗 你应该使用DCPcrypt v2 http www cityinthesky co uk cryptography html读取缓冲
  • 无论线程如何,对象是否总是能看到其最新的内部状态?

    假设我有一个带有简单整数计数变量的可运行对象 每次可运行对象运行时该变量都会递增 该对象的一个 实例被提交以在计划的执行程序服务中定期运行 class Counter implements Runnable private int coun
  • Boost:如何创建一个线程以便可以控制它的所有标准输出、标准错误?

    我用 C 创建了一个 win32 控制台应用程序 我使用一些API 不是我的 我不能修改它的来源 它是这样写的 它会将一些信息写入控制台屏幕 而不询问 每次我调用它时 每秒 48 次 所以我想将它放入某个线程并限制其输出能力 但我需要得到当

随机推荐

  • 从直方图曲线中选择最佳值范围

    设想 我正在尝试跟踪两个不同颜色的物体 一开始 系统会提示用户将第一个彩色对象 例如 可能是红色 放在相机前面的特定位置 在屏幕上用矩形标记 并按任意键 然后我的程序将获取帧的该部分 ROI 并分析其中的颜色 找到要跟踪的颜色 对于第二个对
  • Edge chromium 不会显示基本身份验证弹出窗口?

    我有一个 Apache 服务器 v2 4 43 为我的网站提供服务 并且我使用一个简单的 htpasswd 我使用指令 AuthUserFile 在 htaccess 中调用它来进行身份验证 现在 该解决方案适用于所有浏览器 将显示一个弹出
  • 应用程序内的 Dropbox 身份验证

    有什么方法可以在 iPhone 中的 Dropbox 应用程序中对用户进行身份验证吗 I using Dropbox IOS https www dropbox com developers start authentication ios
  • 如果一个同步方法调用另一个非同步方法,该非同步方法是否有锁

    在Java中 如果一个同步方法包含对非同步方法的调用 那么另一个方法是否仍然可以同时访问该非同步方法 基本上我要问的是同步方法中的所有内容都有锁 包括对其他同步方法的调用 如果一个同步方法调用另一个非同步方法 该非同步方法是否有锁 答案取决
  • 仅在第一次使用 Rspec 调用时存根方法

    如何仅在第一次调用时存根方法 而在第二次调用中它应该按预期运行 我有以下方法 def method do stuff rescue gt MyException sleep rand retry end 我想要的第一个电话do stuff募
  • const 多维数组初始化

    为什么下面的方法有效 class A public int i 1 2 3 1 2 3 1 2 3 static void Main string args 而以下则不然 class A public const int i 1 2 3 1
  • 我可以使用 OkHttp 将本地 IP 地址绑定到我的 SSLSocketFactory 吗?

    我正在努力让 Android 上的 OkHttpClient 使用自定义证书发出 HTTPS 请求 同时绑定到特定网络接口的本地地址 我目前的尝试使用以下内容OkHttpClient val client OkHttpClient Buil
  • 将 pnglib 中的数据显示为 ximage

    我需要导入 PNG 并将其显示在 Motif 应用程序的屏幕上 由于我自己最清楚的原因 我不想使用超出需要的库 并且我想只使用 Motif 和 pnglib 我已经为此奋斗了几天 我想放下我的骄傲并寻求一些帮助 此屏幕截图显示了问题 htt
  • 检查约束不适用于超过 250 条记录的批量插入

    我的查询 INSERT into PriceListRows PriceListChapterId No SELECT TOP 250 100943 N 2 FROM AnyTable 该查询工作正常 并且根据需要引发以下异常 INSERT
  • Java 流具有多个不同的属性

    我在流中有以下对象 class Foo String a String b int c 我想根据以下条件过滤流 例如 流中有条目 foo1 and foo2 foo1 and foo2具有相同的值a and b 但它们的不同之处在于c财产
  • 下面代码的时间复杂度?

    有人可以告诉我以下代码的时间复杂度吗 include
  • 使用 Jquery 的多级下拉菜单

    我想使用 jQuery 设计一个多级菜单 我已经写了一些代码 你可以看demohere http jsfiddle net 24ZvL 这一切都运行良好 但我想动态制作多级下拉菜单 Script ul menu gt li hover fu
  • Android getContext 在后台服务上

    我正在尝试创建一个Service即使我的应用程序关闭 它也会运行 但是 我需要使用我的应用程序Context里面这个Service 当应用程序运行时 该服务也可以工作 但是当我关闭应用程序 调用了 onDestroy 时 getContex
  • Safari 上的 facebook 应用程序 iframe 登录问题

    我有一个使用 iframe 的 Facebook 应用程序 facebook 在 iframe 中加载我的网站 当我单击链接时 我的网站会显示一个 iframe 使用 lightbox 来显示 Facebook 登录信息 在 ff 即 ch
  • 将访问令牌存储在客户端浏览器的会话存储中是否安全?

    我正在 Web API 中使用基于令牌的身份验证来对用户进行身份验证 我正在使用客户端浏览器会话存储来存储访问令牌 这样做安全吗 我应该把它存放在哪里才能更安全 btnLogin click function ajax Post usern
  • 我的 jQuery 切换类函数正在触摸屏设备上创建超链接效果

    当鼠标悬停在我的网站标题上时 我使用 jQuery 添加了 css 过渡效果 使其从透明背景变为白色背景 添加附加类时 我在触摸屏设备上遇到了一个非常奇怪且意想不到的问题 active 我只能假设这种情况发生在所有触摸屏设备上 因为我只有
  • 在每个循环中从 ArrayList 中删除对象

    我想从一个对象中删除一个对象ArrayList当我完成它时 但我找不到方法 尝试像下面的示例代码一样删除它是行不通的 我怎样才能到达当前的迭 代器px要删除这个循环中的对象吗 for Pixel px pixel if px y gt gH
  • 我应该选择哪个 C++ 信号/槽库? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在不使用 QT 的项目中使用信号 槽库 我有非常基本的要求 使用任意数量的参数连接两个函数 信号可以
  • 删除不与 JpaRepository 一起使用的内容

    我有一个 spring 4 应用程序 我试图从数据库中删除实体的实例 我有以下实体 Entity public class Token implements Serializable Id SequenceGenerator name se
  • Delphi多线程文件写入:I/O错误32

    我创建了一个类 用于在文本文件中写入线程安全日志 使用CriticalSection 我不是 CriticalSection 和多线程编程 和 Delphi 的专家 我肯定做错了什么 unit ErrorLog interface uses