zoukankan      html  css  js  c++  java
  • 一个FileSystemWatcher和线程池的问题

    说下有问题的程序,首先建立一个FileSystemWatcher,监控目录是否有新的文件到达,如果到达了就线程池分配一个线程来读取文件,然后进行后续处理,思路很简单,代码如下:

       1: private readonly FileSystemWatcher _watcher;
       2:  
       3: public IntrayWatcher(string path)
       4: {           
       5:     _watcher = new FileSystemWatcher
       6:                   {
       7:                       Path = path,
       8:                       Filter = Constants.Configuration.ExcelFilter,
       9:                       NotifyFilter = NotifyFilters.FileName |
      10:                                      NotifyFilters.LastWrite |
      11:                                      NotifyFilters.CreationTime
      12:                   };
      13:  
      14:     _watcher.Created += OnNewFileComesin;
      15:     _watcher.EnableRaisingEvents = true;
      16: }
      17:  
      18: private void OnNewFileComesin(object sender, FileSystemEventArgs e)
      19: {
      20:     Task.Factory.StartNew(() =>
      21:               {   
      22:                   return ReadFile(e.FullPath);
      23:               })
      24:               .ContinueWith( 
      25:                 //....
      26:                );
      27: }
      28:  
      29: private object ReadFile(string filePath)
      30: {
      31:    using (var stream = new StreamReader(fullPath))
      32:    {
      33:                 //...
      34:                 return data;
      35:    }
      36: }

    思路很清晰,代码很清晰,实际运行发现一个bug,那就是第一次往文件夹里面Copy一个新文件能正常运行,但是Copy第二个文件的时候就报一个文件正被其他线程占用无法打开的异常:

    12-23-2011 11-53-32

    想想也想不通,哪里来的其他进程也打开了这个文件,导致文件无法打开?后来发现,原来是FileSystemWatcher有个问题(也可以说bug吧),就是当新文件到达了以后,这个Watcher太灵敏,文件到达了,IO拷贝还没有完成,其Created事件就已经触发了。也就是说,FileSystemWatcher的Created事件不是在新文件到达拷贝完成的时候触发的,而是这个事件在文件一创建还没有IO拷贝完成的时候就触发了。这就造成两个线程在读取同一个文件的异常了。

    为什么第二次才出现问题?

    为什么第一次文件拷贝进来就能正常工作, 而当拷贝进来第二个文件就报异常呢? 而且我还发现第一次即使一次同时拷贝N个文件进来也没有问题。这是为什么?想了一会儿,原来是这样的:

    当第一个文件进来的时候,Task.Factory.StartNew这句话是线程池创建新的线程,这个操作是异步的,创建线程,切换线程需要CPU和内存开销,而且过程又是异步的,所以首次会慢一点,不会抢到IO拷贝的进程。第二个文件进来的时候,由于程序空闲,Task.Factory.StartNew这句话是线程池分配空闲线程(还是原来那个线程号),这个步骤非常快,就抢到IO拷贝的进程。至于为什么第一次即使一次同时拷贝N个文件进来也没有问题,原因是他们并发到来,当时Task.Factory.StartNew这句话是线程池创建N个不同的新的线程。所以原因和一个文件的时候一样。(不知道我理解正确否?)

    Thread.Sleep()?

    解决办法很简单了,就是在读取文件前加一行:Thread.Sleep(100); 难道解决问题的办法就是让线程睡觉?看到老外也有这样的,原理一样的:

       1: private void WaitForFile(string fullPath)
       2: {
       3:     while (true)
       4:     {
       5:         try
       6:         {
       7:             using (var stream = new StreamReader(fullPath))
       8:             {
       9:                 break;
      10:             }
      11:         }
      12:         catch
      13:         {
      14:             Thread.Sleep(100);
      15:         }
      16:     }
      17: }

    不知道大家有没有其它办法呢?

  • 相关阅读:
    TP5学习笔记- 使用命令行创建控制器
    centos 7 下安装mysql5.7
    webserver的安装
    linux常用命令 服务器硬件资源信息
    SSH 安装/ config 配置以及免密码登录
    thinkphp ,laravel,yii2运行环境搭建.
    分享几个博客园代码样式的CSS配置(复制黏贴即可)
    vue中通过.sync修饰符实现子组件修改父组件数据
    vue中$attrs和$listeners以及inheritAttrs的用法
    Vue项目中实现用户登录及token验证
  • 原文地址:https://www.cnblogs.com/Mainz/p/2299232.html
Copyright © 2011-2022 走看看