windows 如何避免 GetFileAttributes 中的网络停顿?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1142080/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to avoid network stalls in GetFileAttributes?
提问by Eric Grange
I'm testing the existence of a file in a remote share (on a Windows server). The underlying function used for testing is WinAPI's GetFileAttributes, and what happens is that function can take an inordinate amount of time (dozens of seconds) in various situations, like when the target server being offline, when there are rights or DNS issues, etc.
我正在测试远程共享中文件的存在(在 Windows 服务器上)。用于测试的底层函数是 WinAPI 的GetFileAttributes,发生的情况是该函数在各种情况下可能会花费过多的时间(数十秒),例如目标服务器离线、存在权限或 DNS 问题等。
However, in my particular case, it's always a LAN access, so if the file can't be accessed in less than 1 second, then it typically won't be accessible by waiting dozens of seconds more...
但是,在我的特定情况下,它始终是 LAN 访问,因此如果无法在 1 秒内访问该文件,则通常无法通过再等待数十秒来访问它...
Is there an alternative to GetFileAttributes that wouldn't stall? (apart from calling it in a thread and killing the thread after a timeout, which seems to bring its own bag of issues)
有没有不会停止的 GetFileAttributes 替代方案?(除了在线程中调用它并在超时后杀死线程,这似乎带来了自己的问题)
采纳答案by MSalters
The problem isn't GetFileAttributes really. It typically uses just one call to the underlying file system driver. It's that IO which is stalling.
问题实际上不在于 GetFileAttributes。它通常只调用一次底层文件系统驱动程序。正是那个 IO 正在停滞。
Still, the solution is probably easy. Call CancelSynchronousIo()after one second (this obviously requires a second thread as your first is stuck inside GetFileAttributes).
不过,解决方案可能很简单。一秒钟后调用CancelSynchronousIo()(这显然需要第二个线程,因为您的第一个线程卡在 GetFileAttributes 中)。
回答by Sam Harwell
One cool thing about delegates is you can always BeginInvoke
and EndInvoke
them. Just make sure the called method doesn't throw an exception out since [I believe] it will cause a crash (unhandled exception).
关于代表的一件很酷的事情是你总是可以BeginInvoke
和EndInvoke
他们。只要确保被调用的方法不会抛出异常,因为[我相信]它会导致崩溃(未处理的异常)。
AttributeType attributes = default(AttributeType);
Action<string> helper =
(path) =>
{
try
{
// GetFileAttributes
attributes = result;
}
catch
{
}
};
IAsyncResult asyncResult = helper.BeginInvoke();
// whatever
helper.EndInvoke();
// at this point, the attributes local variable has a valid value.
回答by Ian Boyd
I think your best solution is to use a thread-pool thread to perform the work.
我认为您最好的解决方案是使用线程池线程来执行工作。
- assign a unit of work to query the attributes of a file
- let
GetFileAttributes
run to completion - post the results back to your form
- when your thread function completes, the thread automatically returns back to the pool (no need to kill it)
- 分配一个工作单元来查询文件的属性
- 让
GetFileAttributes
运行完成 - 将结果发布回您的表单
- 当您的线程函数完成时,该线程会自动返回到池中(无需杀死它)
By using the thread pool you save the costs of creating new threads.
And you save the misery of trying to get rid of them.
通过使用线程池,您可以节省创建新线程的成本。
并且您省去了试图摆脱它们的痛苦。
Then you have your handy helper method that runs an object's method procedure on a thread-pool thread using QueueUserWorkItem
:
然后你有你方便的辅助方法,它使用以下方法在线程池线程上运行对象的方法过程QueueUserWorkItem
:
RunInThreadPoolThread(
GetFileAttributesThreadMethod,
TGetFileAttributesData.Create('D:\temp\foo.xml', Self.Handle),
WT_EXECUTEDEFAULT);
You create the object to hold the thread data information:
您创建对象来保存线程数据信息:
TGetFileAttributesData = class(TObject)
public
Filename: string;
WndParent: HWND;
Attributes: DWORD;
constructor Create(Filename: string; WndParent: HWND);
end;
and you create your thread callback method:
然后创建线程回调方法:
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;
then you just handle the message:
然后你只需处理消息:
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;
The magical RunInThreadPoolThread
is just a bit of fluff that lets you execute an instance method in a thread:
神奇的RunInThreadPoolThread
是,它只是让你在线程中执行实例方法的一些绒毛:
Which is just a wrapper that lets you call method on an instance variable:
这只是一个包装器,可让您在实例变量上调用方法:
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;
Exercise for the reader: Create a Cancelledflag inside the GetFileAttributesData
object that tells the thread that itmust free the data object and notpost a message to the parent.
读者练习:在对象内创建一个Canceled标志GetFileAttributesData
,告诉线程它必须释放数据对象而不是向父对象发送消息。
It's all a long way of saying that you're creating:
总而言之,您正在创造:
DWORD WINAPI GetFileAttributes(
_In_ LPCTSTR lpFileName,
_Inout_ LPOVERLAPPED lpOverlapped,
_In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);