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

尽管线程同步C,但仍发生IOException#

  •  0
  • kovac  · 技术社区  · 8 年前

    我在同一个类中声明了两个事件处理程序(我们称之为 WrapperClass ):一个用于将文件保存到文件夹,另一个用于将这些文件发送到web api。在应用程序主线程中,我在应用程序启动时调用了两种方法:

    // save file to folder:
    NewFileEventHandler();
    // send file to api:
    FileTransporter();
    

    NewFileEventHandler 定义如下:

    public void NewFileEventHandler()
    {
        SomeEventClass.NewFileEvent +=
             new FileEventHandler(SaveFileToFolder);
    }
    
    private void SaveFileToFolder(File file)
    {
        FileHelper.SaveFileToFolder(file);
    }
    

    FileTransporter 定义如下,这就是我的问题所在:

    public void FileTransporter()
    {
         FileSystemWatcher newFileWatcher = new FileSystemWatcher();
         newFileWatcher.Path = ConfigurationHelper.applicationRootDirectory;
         newFileWatcher.Filter = "*.txt";
         newFileWatcher.Created +=
         new FileSystemEventHandler(TransportFile);
         newFileWatcher.EnableRaisingEvents = true;
    }
    
    And the `TransportFile()` is given below:
    
    private void TransportFile(object source, FileSystemEventArgs e)
    {
        lock (_fileTransportLock)
        {
             string[] files = new string[] { };
             files = Directory.GetFiles(ConfigurationHelper.applicationRootDirectory, "*.txt", SearchOption.TopDirectoryOnly);
             Parallel.ForEach(files, (currentFile) =>
             {
                 bool isHttpTransferSuccess = false;
    
                 isHttpTransferSuccess = FileHelper.SendFileToApi(userid, currentFile);
                 if (isHttpTransferSuccess)
                 {
                     File.Delete(currentFile);
                 }
            });
         }
    }
    

    但是,该行:

    引发异常:

    System.IO.IOException: The process cannot access the file 'C:\Users\myapp\file.txt' because it is being used by another process.
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
       at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
       at System.IO.File.Open(String path, FileMode mode)
       at FileHelper.SendFileToApi(String userId, String fileLocation)
    

    我不明白的是,因为 lock 唯一可能使用此文件的两个进程是保存文件的线程和尝试将文件发送到api的线程。然而,我对 FileSystemWatcher.Created 事件是指它在文件创建完成时触发。也就是说,保存文件的线程在 TransportFile() 方法尝试打开文件以将其发送到api。

    有时文件夹中有多个文件(由于过去丢失了电子邮件)。这个 IOException 仅针对刚刚保存到文件夹的文件(换句话说,引发 FileSystemWatcher。创建 事件按预期清除文件夹中的其他文件。有人能帮忙吗?谢谢

    1 回复  |  直到 8 年前
        1
  •  3
  •   Chris Shain    8 年前

    您在这里缺少一些东西:

    1. 您正在挂接的事件是 FileCreated . 当文件由其他进程创建时,会触发此事件(可能不足为奇), 当其他进程完成文件写入时。这里发生的情况是,当另一个进程仍在写入文件时,您的进程收到通知,并且对其具有独占锁定。从…起 the documentation :

    一旦创建文件,就会引发OnCreated事件。如果是文件 正在复制或传输到监视的目录中 事件将立即引发,然后是一个或多个OnChanged 事件。

    1. 创建文件后,您将遍历目录中的所有文件(不仅仅是刚刚创建的文件)并传输所有文件。如果创建了多个文件,则对事件的第一次调用将尝试访问目录中的任何文件(实际上是并行的),因此,即使1不是问题,在处理第一次事件时,您也可能与正在写入的另一个文件发生冲突。

    正确的做法是循环,直到您能够读取文件,如第二个答案所述: Wait for file to be freed by process