代码之家  ›  专栏  ›  技术社区  ›  OldSchool

有没有办法知道FileSystemwatcher还有多少缓冲区?

  •  3
  • OldSchool  · 技术社区  · 7 年前

    当我询问时

    fileSystemWatcher.InternalBufferSize
    

    它将给出分配给观察程序的总内部缓冲区大小。但我想知道(在调试过程中)为观察程序剩下多少缓冲区大小,可以使用多少缓冲区大小,当我在事件处理程序方法中使用上述语句(比如写操作)时,它总是给我分配给观察程序的总缓冲区大小。有没有办法获得缓冲区的剩余大小?

    从…起 this 回答:很明显,事件是在单独的线程上处理的,而不是在接收事件的线程上处理的。假设我们有许多并发事件发生在一个正在监视文件的观察者身上。我认为(如果我错了,请纠正我)接收事件信息的主线程将为每个事件生成一个新线程,并且事件处理将在不同的线程上进行。所以我想问:

    1. 我在很多地方都读到过,handler方法应该花费尽可能少的时间,或者我们可以得到 InternalBufferOverflow 例外 那么,假设只有当正在处理处理程序方法的线程(我不能说一个或全部,但想问您)已经处理了该方法时,才清理观察程序的内部缓冲区,这样安全吗?
    1 回复  |  直到 7 年前
        1
  •  2
  •   rene    7 年前

    不,你不知道还有多少缓冲区。

    它是隐藏在名为 FSWAsyncResult 因为该字节数组仅作为调用的结果的保留内存 ReadDirectoryChangesW

    在这个答案的底部可以找到一个精简的反向工程版本,用于监视文件夹中的文件更改。它的逻辑和代码与您在真正的FileSystemWatcher中找到的内容相匹配。我没有费心用它们的正确含义来替换神奇常数。 。不要忘记更改生成设置 不安全的 因为代码经常篡改指针和本机结构。我去掉了所有的错误处理。。。

    worker and I/O threads 我理解 ReadDirectoryChangesW

    回调调度在托管线程池线程上。有时,你会得到与之前相同的托管id,当它很忙时,你会得到几个。在那条线上 CompletionStatusChanged sizeused Monitor 再次使用 相同字节[]缓冲区 完成状态已更改 正在执行的由操作系统保留,并在下次发送 被称为。

    tl;博士 以下是对您的问题的回答:

    1. 哪个线程将清除与观察程序相关的内部缓冲区,以及何时?

    1. 我在很多地方读到,处理程序方法应该花费尽可能少的时间,否则我们可以得到InternalBufferOverflow异常。那么,假设只有当正在处理处理程序方法的线程(我不能说一个或全部,但想问您)已经处理了该方法时,才清理观察程序的内部缓冲区,这样安全吗?

    ReadDirectoryChangesW 再一次在此期间,它将跟踪文件更改。当这些filechange事件不适合缓冲区时,它将在下次调用完成方法时引发InternalBufferOverflow。

    安装程序

    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();
    
    }
    

    班长

    该方法负责创建本机结构和助手类的实例,作为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方法。在重叠结构中,解包后,我们将发现我们早期的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);
        }
    }
    

    助手方法


    NativeMethods正是它们所称的:WinAPI的本机调用的入口点。

    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);
    }