继续TEvent,破事真不少

CreateEvent函数的坑
本文通过测试CreateEvent函数创建大量事件对象的过程,揭示了在Windows平台下频繁调用该函数可能导致的各种异常错误。作者尝试创建1024个事件对象时遇到了不可预料的错误,推测这可能与内核级函数的限制、性能调度策略及进程同步机制有关。

在Windows平台上,TEvent的核心是CreateEvent函数,因此咱们就直奔主题吧。

本来是想测试一下CreateEvent的内存占用的,以为一个THandle只有4个字节(Win32)的内存占用,应该不会有别的事情发生,于是就简单写了以下代码:

procedure TForm1.Button2Click(Sender: TObject);
var
  i, iCount: Integer;
  FEvents: array of THandle;
begin
  iCount := 1024;


  SetLength(FEvents, 1);
  for i := 0 to iCount - 1 do
    FEvents[i] := CreateEvent(nil, True, False, nil);


  Sleep(2000);


  for i := 0 to iCount - 1 do
    CloseHandle(FEvents[i]);
  SetLength(FEvents, 0);

end;

运行一下,然后奇葩的事情就来了,各种花样的AV错误全蹦出来了,还是变着法儿的错误,每次都不一样的那种。。。好吧,我还想着直接用1M的测试呢,这样看来是不行了。

那么问题来了,怎么会这样呢?经查MSDN和网络,也没有什么地方提到这个问题,于是只能自己想当然的解释一下吧:

1.CreateEvent是内核级的函数,应该有限制但MS又没有说(或者可创建的内核对象有限制但我没找到),很多人没遇到自然就不管了。

2.CreateEvent的调度应该是性能优先的,因此很多时候不接受一大堆等不了的事件。

3.CreateEvent更重要的一点是可以同步进程的,自然同2一样,也不大接受一大堆无聊的等待。

所以问题来了,自己编写程序时还是要注意一下CreateEvent的限制,不能想当然的疯狂使用系统资源,说不定哪天莫名其妙就挂了,并且还找不到原因。

以上是自己看到的现象,还没有啥完美解释,只能先记录下来吧。留给其他有需要的同学继续做参考和研究。

最后说一句:其实写原生服务器程序就是自己跟自己狭气,自己给操作系统出难题,各种刁难操作系统,既想让操作系统玩儿命干活又不想让操作系统崩掉。其实操作系统心里想的是:我有一句mmp不知当讲不当讲。。。


Delphi 12.3 的多线程编程中,结合 `TEvent`、`TQueue` 和 `TMonitor` 可以实现线程间的安全通信和高效同步。这种组合特别适用于生产者-消费者模型,其中多个线程需要安全地向队列中添加或取出数据。 ### 使用 `TQueue` 实现线程安全的数据队列 `TQueue<T>` 是一个泛型队列类,提供线程安全的基本操作如 `Enqueue` 和 `Dequeue`。虽然 `TQueue` 本身不是完全线程安全的,但可以通过 `TMonitor` 对其进行同步保护,以确保多个线程同时访问时数据一致性[^1]。 以下是一个封装了 `TQueue` 的类示例,通过 `TMonitor` 保证线程安全: ```delphi type TThreadSafeQueue<T> = class private FQueue: TQueue<T>; FMonitor: TMonitor; public constructor Create; destructor Destroy; override; procedure Enqueue(const Item: T); function Dequeue: T; function Count: Integer; end; constructor TThreadSafeQueue<T>.Create; begin inherited; FQueue := TQueue<T>.Create; FMonitor := TMonitor.Create; end; destructor TThreadSafeQueue<T>.Destroy; begin FMonitor.Free; FQueue.Free; inherited; end; procedure TThreadSafeQueue<T>.Enqueue(const Item: T); begin FMonitor.Enter(Self); try FQueue.Enqueue(Item); finally FMonitor.Exit(Self); end; end; function TThreadSafeQueue<T>.Dequeue: T; begin FMonitor.Enter(Self); try if FQueue.Count > 0 then Result := FQueue.Dequeue else raise Exception.Create('Queue is empty'); finally FMonitor.Exit(Self); end; end; function TThreadSafeQueue<T>.Count: Integer; begin FMonitor.Enter(Self); try Result := FQueue.Count; finally FMonitor.Exit(Self); end; end; ``` ### 使用 `TEvent` 实现线程间同步等待 `TEvent` 是一个用于线程间同步的内核对象,通过 `WaitFor`、`SetEvent` 和 `ResetEvent` 方法实现线程等待和唤醒机制。在线程等待队列数据时,可以使用 `TEvent` 阻塞线程,直到有新数据到达。 以下是一个结合 `TThreadSafeQueue` 和 `TEvent` 的消费者线程示例: ```delphi type TConsumerThread = class(TThread) private FQueue: TThreadSafeQueue<string>; FEvent: TEvent; protected procedure Execute; override; public constructor Create(AQueue: TThreadSafeQueue<string>; AEvent: TEvent); end; constructor TConsumerThread.Create(AQueue: TThreadSafeQueue<string>; AEvent: TEvent); begin inherited Create(False); FQueue := AQueue; FEvent := AEvent; end; procedure TConsumerThread.Execute; var Item: string; begin while not Terminated do begin if FQueue.Count = 0 then begin FEvent.WaitFor(INFINITE); // 等待件触发 FEvent.ResetEvent; // 重置件为无信号状态 end; if FQueue.Count > 0 then begin Item := FQueue.Dequeue; TThread.Synchronize(nil, procedure begin Writeln('Consumed: ' + Item); end ); end; end; end; ``` 生产者线程在向队列中添加数据后,调用 `SetEvent` 唤醒消费者线程: ```delphi procedure ProduceData(AQueue: TThreadSafeQueue<string>; AEvent: TEvent); begin AQueue.Enqueue('Data Item'); AEvent.SetEvent; // 唤醒等待的消费者线程 end; ``` ### 完整流程示例 主程序中创建队列、件和线程: ```delphi var Queue: TThreadSafeQueue<string>; Event: TEvent; Consumer: TConsumerThread; begin Queue := TThreadSafeQueue<string>.Create; Event := TEvent.Create; try Consumer := TConsumerThread.Create(Queue, Event); Consumer.FreeOnTerminate := True; Consumer.Start; // 模拟生产数据 ProduceData(Queue, Event); Readln; Consumer.Terminate; finally Queue.Free; Event.Free; end; end. ``` ### 总结 通过 `TMonitor` 保护 `TQueue` 的访问,`TEvent` 控制线程的等待与唤醒,实现了线程间的安全通信。这种模式适用于需要异步处理任务、数据传递和件驱动的多线程应用,具有良好的扩展性和稳定性。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值