Tôi nghĩ giải pháp tốt nhất của bạn là sử dụng một chuỗi chủ đề hồ bơi để thực hiện công việc.
- gán một đơn vị làm việc để truy vấn các thuộc tính của một tập tin
- hãy
GetFileAttributes
chạy để hoàn
- gửi kết quả trở lại hình thức của bạn
- khi chức năng thread của bạn hoàn tất, các chủ đề tự động trả lại vào hồ bơi (không cần phải giết nó)
Bằng cách sử dụng nhóm chủ đề bạn tiết kiệm chi phí tạo chuỗi mới.
Và bạn tiết kiệm được nỗi đau khi cố gắng loại bỏ chúng.
Sau đó, bạn có phương pháp helper tiện dụng của bạn chạy thủ tục phương pháp của một đối tượng trên một sợi thread-pool sử dụng QueueUserWorkItem
:
RunInThreadPoolThread(
GetFileAttributesThreadMethod,
TGetFileAttributesData.Create('D:\temp\foo.xml', Self.Handle),
WT_EXECUTEDEFAULT);
Bạn tạo các đối tượng để giữ thông tin dữ liệu chủ đề:
TGetFileAttributesData = class(TObject)
public
Filename: string;
WndParent: HWND;
Attributes: DWORD;
constructor Create(Filename: string; WndParent: HWND);
end;
và bạn tạo phương thức gọi lại cho chủ đề của mình:
procedure TForm1.GetFileAttributesThreadMethod(Data: Pointer);
var
fi: TGetFileAttributesData;
begin
fi := TObject(Data) as TGetFileAttributesData;
if fi = nil then
Exit;
fi.attributes := GetFileAttributes(PWideChar(fi.Filename));
PostMessage(fi.WndParent, WM_GetFileAttributesComplete, NativeUInt(Data), 0);
end;
t hen bạn chỉ xử lý các thông điệp:
procedure WMGetFileAttributesComplete(var Msg: TMessage); message WM_GetFileAttributesComplete;
procedure TfrmMain.WMGetFileAttributesComplete(var Msg: TMessage);
var
fi: TGetFileAttributesData;
begin
fi := TObject(Pointer(Msg.WParam)) as TGetFileAttributesData;
try
ShowMessage(Format('Attributes of "%s": %.8x', [fi.Filename, fi.attributes]));
finally
fi.Free;
end;
end;
Các huyền diệu RunInThreadPoolThread
chỉ là một chút lông tơ mà cho phép bạn thực hiện một phương pháp dụ trong một thread:
Mà chỉ là một wrapper cho phép bạn gọi phương thức trên một Ví dụ biến:
TThreadMethod = procedure (Data: Pointer) of object;
TThreadPoolCallbackContext = class(TObject)
public
ThreadMethod: TThreadMethod;
Context: Pointer;
end;
function ThreadPoolCallbackFunction(Parameter: Pointer): Integer; stdcall;
var
tpContext: TThreadPoolCallbackContext;
begin
try
tpContext := TObject(Parameter) as TThreadPoolCallbackContext;
except
Result := -1;
Exit;
end;
try
tpContext.ThreadMethod(tpContext.Context);
finally
try
tpContext.Free;
except
end;
end;
Result := 0;
end;
function RunInThreadPoolThread(const ThreadMethod: TThreadMethod; const Data: Pointer; Flags: ULONG): BOOL;
var
tpContext: TThreadPoolCallbackContext;
begin
{
Unless you know differently, the flag you want to use is 0 (WT_EXECUTEDEFAULT).
If your callback might run for a while you can pass the WT_ExecuteLongFunction flag.
Sure, I'm supposed to pass WT_EXECUTELONGFUNCTION if my function takes a long time, but how long is long?
http://blogs.msdn.com/b/oldnewthing/archive/2011/12/09/10245808.aspx
WT_EXECUTEDEFAULT (0):
By default, the callback function is queued to a non-I/O worker thread.
The callback function is queued to a thread that uses I/O completion ports, which means they cannot perform
an alertable wait. Therefore, if I/O completes and generates an APC, the APC might wait indefinitely because
there is no guarantee that the thread will enter an alertable wait state after the callback completes.
WT_EXECUTELONGFUNCTION (0x00000010):
The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
WT_EXECUTEINPERSISTENTTHREAD (0x00000080)
The callback function is queued to a thread that never terminates.
It does not guarantee that the same thread is used each time. This flag should be used only for short tasks
or it could affect other timer operations.
This flag must be set if the thread calls functions that use APCs.
For more information, see Asynchronous Procedure Calls.
Note that currently no worker thread is truly persistent, although worker threads do not terminate if there
are any pending I/O requests.
}
tpContext := TThreadPoolCallbackContext.Create;
tpContext.ThreadMethod := ThreadMethod;
tpContext.Context := Data;
Result := QueueUserWorkItem(ThreadPoolCallbackFunction, tpContext, Flags);
end;
tập thể dục cho người đọc: Tạo một hủy cờ bên trong đối tượng GetFileAttributesData
mà nói thứ e chủ đề mà nó phải giải phóng đối tượng dữ liệu và không đăng thông báo cho phụ huynh.
Đó là tất cả một chặng đường dài để nói rằng bạn đang tạo:
DWORD WINAPI GetFileAttributes(
_In_ LPCTSTR lpFileName,
_Inout_ LPOVERLAPPED lpOverlapped,
_In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
không thể nghĩ về bất cứ điều gì ngoại trừ mô hình không đồng bộ. –
Trong các trường hợp khác, tôi đã giải quyết vấn đề này bằng cách triển khai một máy chủ web tối giản để phục vụ các tệp được chia sẻ, vì yêu cầu HTTP có thể dễ dàng bị hủy/hết thời gian chờ. Nhưng trong trường hợp này, đây không phải là giải pháp vì nhiều lý do (nhức đầu triển khai, vấn đề bảo mật, v.v.). –