System.Io.Directory::GetFiles() 从 AX 2009 轮询,每 10 秒只看到新文件

System.Io.Directory::GetFiles() Polling from AX 2009, Only Seeing New Files Every 10s

我在 AX 2009 中编写代码以每 1 秒轮询一次网络驱动器上的目录,等待来自另一个系统的响应文件。我注意到使用文件资源管理器 window,我可以看到该文件出现,但我的代码有几秒钟没有看到和处理该文件 - 文件出现后最多 9 秒(和 9 次轮询)!

AX代码调用System.IO.Directory::GetFiles()使用ClrInterop:

interopPerm = new InteropPermission(InteropKind::ClrInterop);
interopPerm.assert();
files = System.IO.Directory::GetFiles(#POLLDIR,'*.csv');
// etc...
CodeAccessPermission::revertAssert();

经过多次实验,我发现程序生命周期中的第一次,我称之为 ::GetFiles(),它开始一个 10 秒周期的概念性 "ticking clock"。只有每 10 秒调用一次才能找到可能出现的任何新文件,尽管它们仍然会报告自第一次调用 ::GetFiles().

以来 10 秒前 "tick" 发现的文件

如果,当我启动程序时,文件不存在,那么所有其他调用 ::GetFiles(),第一次调用后 1 秒,2 秒后,等等,最多 9 秒后, 根本看不到该文件,即使它可能在第一次调用后 0.5 秒后一直坐在那里!

然后,可靠且可重复地,第一次调用后 10 秒的调用将找到该文件。然后从 11 秒到 19 秒的调用都不会看到任何可能出现的新文件,但在第一次调用后 20 秒的调用将可靠地看到任何新文件。以此类推,每 10 秒一次。

进一步调查显示,如果轮询目录在 AX AOS 机器上,则不会发生这种情况,并且会立即找到文件,正如人们所期望的那样,在文件出现在目录中后的调用中。

但是这个 10 的数字是可靠且可重复的,无论我轮询哪个网络驱动器,无论它在哪个服务器上。

我们的网络肯定没有10s的延迟看文件;正如我所说,轮询目录上的文件浏览器 window 会立即看到该文件。

这是怎么回事?

听起来您的问题是由 SMB 缓存引起的 - 来自 this technet page:

Name, type, and ID
Directory Cache [DWORD] DirectoryCacheLifetime

Registry key the cache setting is controlled by HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Lanmanworkstation\Parameters

This is a cache of recent directory enumerations performed by the client. Subsequent enumeration requests made by client applications as well as metadata queries for files in the directory can be satisfied from the cache. The client also uses the directory cache to determine the presence or absence of a file in the directory and uses that information to prevent clients from repeatedly attempting to open files which are known not to exist on the server. This cache is likely to affect distributed applications running on multiple computers accessing a set of files on a server – where the applications use an out of band mechanism to signal each other about modification/addition/deletion of files on the server.


简而言之尝试设置注册表项

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Lanmanworkstation\Parameters\DirectoryCacheLifetime

0

我猜 DAXaholic 的答案是正确的,但您可以尝试其他解决方案,例如

在你的情况下,我宁愿等待文件而不是轮询文件。 使用 FileSystemWatcher 从文件创建到您的进程唤醒之间会有最小的延迟。它使用起来更棘手,但避免轮询是一件 好事 。我从未在网络上使用过它。

感谢@Jan B. Kjeldsen,我已经能够使用 FileSystemWatcher 解决我的问题。这是我在 X++ 中的实现:

class SelTestThreadDirPolling
{
}

public server static Container SetStaticFileWatcher(str _dirPath,str _filenamePattern,int _timeoutMs)
{
    InteropPermission interopPerm;
    System.IO.FileSystemWatcher fw;
    System.IO.WatcherChangeTypes watcherChangeType;
    System.IO.WaitForChangedResult res;
    Container cont;
    str fileName;
    str oldFileName;
    str changeType;
    ;

    interopPerm = new InteropPermission(InteropKind::ClrInterop);
    interopPerm.assert();
    fw = new System.IO.FileSystemWatcher();
    fw.set_Path(_dirPath);
    fw.set_IncludeSubdirectories(false);
    fw.set_Filter(_filenamePattern);

    watcherChangeType = ClrInterop::parseClrEnum('System.IO.WatcherChangeTypes', 'Created');
    res = fw.WaitForChanged(watcherChangeType,_timeoutMs);
    if (res.get_TimedOut()) return conNull();

    fileName = res.get_Name();
    //ChangeTypeName can be: Created, Deleted, Renamed and Changed
    changeType = System.Enum::GetName(watcherChangeType.GetType(), res.get_ChangeType());

    fw.Dispose();
    CodeAccessPermission::revertAssert();

    if (changeType == 'Renamed') oldFileName = res.get_OldName();

    cont += fileName;
    cont += changeType;
    cont += oldFileName;
    return cont;
}

void waitFileSystemWatcher(str _dirPath,str _filenamePattern,int _timeoutMs)
{
    container cResult;
    str filename,changeType,oldFilename;
    ;

    cResult=SelTestThreadDirPolling::SetStaticFileWatcher(_dirPath,_filenamePattern,_timeoutMs);
    if (cResult)
    {
        [filename,changeType,oldFilename]=cResult;
        info(strfmt("filename=%1, changeType=%2, oldFilename=%3",filename,changeType,oldFilename));
    }
    else
    {
        info("TIMED OUT");
    }
}

void run()
{;
    this.waitFileSystemWatcher(@'\myserver\mydir','filepattern*.csv',10000);
}

我应该承认以下构成我的 X++ 实现的基础:

https://blogs.msdn.microsoft.com/floditt/2008/09/01/how-to-implement-filesystemwatcher-with-x/