c# - 有没有办法知道FileSystemwatcher还剩下多少缓冲区?

标签 c# multithreading events buffer filesystemwatcher

当我查询

fileSystemWatcher.InternalBufferSize

它将给出分配给观察程序的内部缓冲区的总大小。但是我想知道(在调试过程中)还剩下并可以使用Watcher多少缓冲区大小,并且当我在事件处理程序方法中使用上述语句(例如写操作)时,它总是为我分配缓冲区的总大小给观察者。有什么方法可以获取缓冲区的剩余容量吗?

其他问题:

this答案中可以明显看出,事件是在与接收事件的线程不同的单独线程上处理的。假设我们有多个并发事件正在针对正在监视文件的单个Watcher进行。我认为(如果我错了,请纠正我)接收事件信息的主线程将为每个事件产生一个新线程,并且事件的处理将在不同的线程上进行。所以我想问:

  1. Will the main thread wait to finish the processing of all the events?
  2. Which thread will clear the internal buffer associated with the Watcher and when?
  3. I have read at lots of places that the handler method should take as minimum time as possible or we can get InternalBufferOverflow Exception. So, is it safe to assume that the Internal Buffer for the Watcher is only cleaned up when the thread(s) (I can't say one or all, but want to ask from you) which are processing the handler method has processed the method?

最佳答案

不,您不知道还剩下多少缓冲区。

它是隐藏在名为FSWAsyncResult的内部类中的实现细节。如果掌握了该类的实例及其包含的缓冲区字节数组,则仍然无法可靠地回答剩余的空间,因为该字节数组仅充当对 ReadDirectoryChangesW 调用的结果的保留内存。

在此答案的底部找到一个精简的反向工程版本,用于监视文件夹中的文件更改。它的逻辑和代码与您在实际的FileSystemWatcher中找到的逻辑和代码相匹配。我没有费心用适当的含义替换魔术常数。可以正常工作™。不要忘记更改构 build 置,因为代码经常摆弄指针和 native 结构,因此不安全。而且我剥离了所有错误处理...

如果遵循下面的代码,您会注意到只有一个地方创建了byte []缓冲区,并且只发生了一次。相同的缓冲区被重复使用。阅读文档,博客和worker and I/O threads,我了解ReadDirectoryChangesW用于以I/O完成方式发出回调。对于托管世界而言,这无关紧要,那只是另一个线程。

回调是在托管线程池线程上安排的。有时,您将获得与以前相同的托管ID,而在繁忙时,您将获得多个。在该线程上执行CompletionStatusChanged。该方法负责处理当前字节缓冲区中存在的所有事件。注意,我包括了一个sizeused变量,因此您可以看到缓冲区中存在的有效数据的实际大小。对于发现的每个事件,它都会同步引发(或调用)事件的订阅者(因此在同一线程上)。一旦完成,它将再次使用刚刚处理的相同byte []缓冲区调用Monitor。在执行CompletionStatusChanged的过程中,任何文件更改都会由OS保留,并在下次调用CompletionStatusChanged时发送。

tl; dr;
以下是对您的问题的答案的概述:

... I want to know (during debugging) how much buffer size for the Watcher is left and can be used



仅使用了一个缓冲区,知道多少已使用或剩余多少是没有意义的。调用事件处理程序后,缓冲区将重置并再次从0开始。当字节缓冲区可以处理的事件更多时,它将引发异常。

  1. Will the main thread wait to finish the processing of all the events?


操作系统将通过IOCompletionPort发出异步回调,但是它将自己显示为普通的托管线程池线程。该线程将处理当前缓冲区中的所有事件并调用事件处理程序。

  1. Which thread will clear the internal buffer associated with the Watcher and when?


执行CompletionStatusChanged方法的线程。请注意,在我的测试中,缓冲区从未被清除(因为填充了零)。数据刚刚被覆盖。

  1. I have read at lots of places that the handler method should take as minimum time as possible or we can get InternalBufferOverflow Exception. So, is it safe to assume that the Internal Buffer for the Watcher is only cleaned up when the thread(s) (I can't say one or all, but want to ask from you) which are processing the handler method has processed the method?


您应该使处理尽可能短,因为只有一个线程可以调用所有事件处理程序,最后它必须再次调用ReadDirectoryChangesW。在此期间,它将跟踪文件更改。当这些文件更改事件不适合缓冲区时,它将在下次调用完成方法时引发InternalBufferOverflow。

设置

一个简单的控制台应用程序,带有ReadLine,可在等待事件时保持运行。
static object instance = new object(); // HACK
static SafeFileHandle hndl; // holds our filehandle (directory in this case)

static void Main(string[] args)
{

    // the folder to watch
    hndl = NativeMethods.CreateFile(@"c:\temp\delete", 1, 7, IntPtr.Zero, 3, 1107296256, new SafeFileHandle(IntPtr.Zero, false));
    // this selects IO completion threads in the ThreadPool
    ThreadPool.BindHandle(hndl);

    // this starts the actual listening
    Monitor(new byte[4096]);

    Console.ReadLine();

}

监视器

此方法负责创建 native 结构和作为IAsyncResult实现的帮助程序类的实例。
此方法还调用ReadDirectoryChangesW,并选择参数组合,将其设置为使用IOCompletinPorts进行异步完成。可以在Understanding ReadDirectoryChangesW - Part 1中找到有关这些选项的更多背景信息。
static unsafe void Monitor(byte[] buffer)
{

    Overlapped overlapped = new Overlapped();

    // notice how the buffer goes here as instance member on AsyncResult.
    // Arrays are still Reference types.      
    overlapped.AsyncResult = new AsyncResult { buffer = buffer };
    // CompletionStatusChanged is the method that will be called
    // when filechanges are detected
    NativeOverlapped* statusChanged = overlapped.Pack(new IOCompletionCallback(CompletionStatusChanged), buffer);

    fixed (byte* ptr2 = buffer)
    {
        int num;
        // this where the magic starts
        NativeMethods.ReadDirectoryChangesW(hndl, 
          new HandleRef(instance, (IntPtr)((void*)ptr2)), 
          buffer.Length, 
          1, 
          (int)(NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Attributes), 
          out num, 
          statusChanged, 
          new HandleRef(null, IntPtr.Zero));
    }

}

CompletionStatusChanged

一旦检测到文件更改,操作系统就会立即调用CompletionStatusChanged方法。在Overlapped结构中,我们将在解压缩后找到带有填充缓冲区的早期ResultAsync实例。然后,该方法的其余部分通过读取数组中任何后续事件的偏移量以及标志和文件名来解码字节数组。
// this gets called by a ThreadPool IO Completion thread
static unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped* overlappedPointer)
    {
        var sb = new StringBuilder();

        Overlapped overlapped = Overlapped.Unpack(overlappedPointer);
        var result = (AsyncResult) overlapped.AsyncResult;

        var position = 0;
        int offset;
        int flags;
        int sizeused = 0;
        string file;
        // read the buffer,
        // that can contain multiple events
        do
        {
            fixed (byte* ptr = result.buffer)
            {
                // process FILE_NOTIFY_INFORMATION
                // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa364391(v=vs.85).aspx
                offset = ((int*)ptr)[position / 4];
                flags = ((int*)ptr + position / 4)[1];
                int len = ((int*)ptr + position / 4)[2];
                file = new string((char*)ptr + position / 2 + 6, 0, len / 2);
                sizeused = position + len + 14; 
            }
            sb.AppendFormat("#thread {0},  event: {1}, {2}, {3}, {4}\r\n", Thread.CurrentThread.ManagedThreadId, position, offset, flags, file);
            // in the real FileSystemWatcher here the several events are raised
            // so that uses the same thread this code is on.
            position += offset;
        } while (offset != 0);

        // my own logging
        sb.AppendFormat(" === buffer used: {0} ==== ", sizeused);

        Console.WriteLine(sb);

        // start again, reusing the same buffer:
        Monitor(result.buffer);
    }
}

辅助方法

AsyncResult实现IAsyncResult(全部为空),并将该成员保留在字节数组缓冲区中。
NativeMethods正是它们的称呼:WinAPI中 native 调用的入口点。
class AsyncResult : IAsyncResult
{
    internal byte[] buffer;
    // default implementation of the interface left out

    // removed default implementation for brevity
}

static class NativeMethods
{
    [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto)]
    public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, SafeFileHandle hTemplateFile);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public unsafe static extern bool ReadDirectoryChangesW(SafeFileHandle hDirectory, HandleRef lpBuffer, int nBufferLength, int bWatchSubtree, int dwNotifyFilter, out int lpBytesReturned, NativeOverlapped* overlappedPointer, HandleRef lpCompletionRoutine);
}

关于c# - 有没有办法知道FileSystemwatcher还剩下多少缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45586630/

相关文章:

c++ - 避免多个互斥锁以保护类似的竞争条件

c++ - 是什么占用了我的工作时间?

java - Eratosthenes 的素数顺序比同时更快?

jquery - 为什么 jQuery 在加载某些内容后会丢失 'event'(单击)?

c# - 将 Emit IL 与内部类一起使用?

c# - xamarin.forms-页面底部的“滑动”信息框

c# - 如何使用 JsonObject 类?

c# - 在 ListView 中绑定(bind)图像只显示字符串

javascript - 在js中使用事件构造函数的一个很好的例子是什么?

javascript - $(window).focus() 在 Chrome 中未正确执行