【发布时间】:2016-02-14 20:12:21
【问题描述】:
我开发了一个接收 TStream 的程序;但基本类型,允许发送所有类型的流继承者。
此过程旨在为每个内核或多个线程创建一个线程。每个线程都会对流数据进行详细分析(只读),由于 Pascal 类是通过引用分配的,而不是按值分配的,因此会发生线程冲突,因为读取位置是 intercalará。
为了解决这个问题,我希望该过程完成所有工作,将内存中的最后一个 TStream 加倍,并为其分配一个新变量。这样我可以复制足够数量的 TStream,以便每个线程都有一个唯一的 TStream。结束后非常线程库内存。
注意:程序在 DLL 中,线程工作。
注2:目标是程序做所有必要的服务,即没有调用的代码的干预;您可以轻松地传递一个TStream 数组,而不仅仅是一个TStream。但我不想要它!目的是服务完全由程序提供。
你知道怎么做吗?
谢谢。
补充:
我有一个低级的想法,但我对 Pascal 的了解有限。
- 确定对象在内存中的地址及其大小。
- 在内存中创建一个与原始对象大小相同的新地址。
- 将整个内容(原始)对象复制到这个新地址。
- 我创建了一个指向 TStream 的指针,该指针指向内存中的这个新地址。
这会起作用,还是很愚蠢?如果是,如何操作?请举例!
2º加法:
举个例子,假设程序对加密流进行暴力攻击(只是一个例子,因为它不适用):
场景:8 核 CPU 中的 30GB 文件:
1º - TMemoryStream:
创建 8 个 TMemoryStream 并为每个 TMemoryStreams 复制文件的全部内容。这将导致同时使用 240GB RAM。我考虑了这个错误的想法。此外,它会将处理时间增加到最快的程度,而不使用多线程。我将不得不将整个文件读入内存,然后加载,开始分析它。坏了!
* TMemoryStream 的一个不好的替代方法是将文件以 100MB/核心 (800MB) 的速度缓慢复制到 TMemoryStream,而不是占用内存。所以每个线程看起来只有 100MB,释放内存直到你完成整个文件。但问题是它需要 DLL 中的 Synchronize() 函数,我们知道这行不通,因为我在 Synchronize () DLL freezes without errors and crashes 中提出了问题
2º - TFileStream:
在我看来,这更糟。看,我得到一个 TStream,创建 8 个 TFileStream 并为每个 TFileStream 复制所有 30GB。这很糟糕,因为在磁盘上占用了 240GB,这对于 HDD 来说也是一个很高的价值。 HD中的读写时间(复制)将使多线程的实现比单线程更耗时。坏了!
结论: 以上两种方法都需要使用 synchronize() 将每个线程排队以读取文件。因此,即使在多核 CPU 上,线程也不会同时运行。我知道即使他可以同时访问文件(直接创建几个TFileStream),操作系统仍然enfileiraria线程一次读取文件,因为HDD不是真正的线程安全,他无法读取两个数据同时 。这是硬盘的物理限制!但是,与我手动实现 synchronize() 不同,OS 的排队管理更加有效,并有效地减少了潜在瓶颈。这证明了我克隆 TStream 的想法是合理的,它将与 S.O. 一起离开。管理文件访问队列的所有工作;无需任何干预 - 我知道他会比我做得更好。
示例
在上面的例子中,我要8个线程同时分析同一个Stream,知道线程不知道提供了什么样的Stream,可以是文件Stream,也可以是来自互联网的stream,甚至是一个小的字符串流。主程序将只创建一个Strean,并带有配置参数。一个简单的例子:
TModeForceBrute = (M1, M2, M3, M4, M5...)
TModesFB = set of TModeForceBrute;
TService = record
stream: TStream;
modes: array of TModesFB;
end;
例如,应该可以只分析流 M1,只分析 M2,或同时分析 [M1,M2]。 TModesFB 组合改变了流的分析方式。 数组“modes”中的每个项目,作为一个任务列表,将由不同的线程处理。任务列表示例(JSON 表示):
{
Stream: MyTstream,
modes: [
[M1, m5],
[M1],
[M5, m2],
[M5, m2, m4, m3],
[M1, m1, m3]
]
}
注意:在分析仪中 [m1] + [m2] [m1, m2]。
在节目中:
function analysis(Task: TService; maxCores: integer): TMyResultType; external 'mydll.dll';
在 DLL 中:
// Basic, simple and fasted Exemple! May contain syntax errors or logical.
function analysis(Task: TService; maxCores: integer): TMyResultType;
var
i, processors : integer;
begin
processors := getCPUCount();
if (maxCores < processors) and (maxCores > 0) then
processors := maxCores;
setlength (globalThreads, processors);
for i := 0 to processors - 1 do
// It is obvious that the counter modes in the original is not the same counter processors.
if i < length(Task.modes) then begin
globalThreads[i] := TAnalusysThread.create(true, Task.stream, Task.modes[i])
globalThreads[i].start();
end;
[...]
end;
注意:使用单线程程序运行良好,没有已知错误。
我希望每个线程负责一种分析,我不能在 DLL 中使用 Synchronize()。理解?有足够干净的解决方案吗?
【问题讨论】:
-
感谢您的帮助,@Abelisto。但这对我不起作用。正如我所说,我必须接受任何类型的 Stream,包括 TFileStream,它可能包含小尺寸、几 KB 的文件,也可能包含多 GB 或 TB 的大文件。所以加载到内存中似乎不是一个合适的解决方案。还是谢谢你。
-
对于内存复制的想法,您需要一个类型化的流,例如 TMemoryStream,然后将内存从其数据指针移动到新创建的 TMemoryStream,首先设置大小。但是,我经常发现,如果我需要如此大的内存移动,通常设计中存在缺陷,我可以修复它,这样我根本不需要移动数据。
-
好的,但是您知道如何在实践中做到这一点吗? 1 - 知道对象在内存中的第一个和最后一个字节的地址。为此,需要知道对象在内存中占用的字节数。因此,假设主题被记录在内存中而没有碎片化。因为如果存在碎片,问题就会变得更加复杂。主意? 2 - 即使你没有分片,找到第一个和最后一个字节的位置,我也不知道如何直接访问内存读取和复制每个字节,然后将每个字节记录到一个新的内存地址中。
-
我最初认为是指向字节(PByte数组)的指针数组。但我不知道具体如何实现。
标签: copy clone pascal lazarus tstream