简体   繁体   中英

Monitoring File lock release with a timeout in multithreading context

I am using FileSystemWatcher to notify me whenever a file arrives in a folder of my system.

Sometimes, they arrive with a lock (by some other program)

I want to perform something like below:

If they are still locked even after the TIMEOUT we ignore that file or if they become lock free within the TIMEOUT we process the file

I currently came up with this solution but wondering if there are any other ways to achieve it.

 lockedFilePaths = new List<string>();
 NoLockFilePaths = new List<string>();

 Watcher.Created += (sender, e) => WatcherHandler(e.FullPath);

 WatcherHandler(string FilePath)
 {
     CheckFileAccess(FilePath);
     if (NoLockFilePaths.Any())
     {
          //Process all file paths
     }
 }

 CheckFileAccess(string filepath)
 {
         // start a timer and invoke every say 10ms
         // log the locked time of the file.
         // compare the current time.
         // return null if TIMEOUT exceeds
         // or wait till the TIMEOUT and keep on checking the file access 
 }

The question is how to implement CheckFileAccess simple and optimal?

I currently use System.Threading.Timer to notify me every 1000 ms, check if the file is still locked and my implementation is not satisfactory to me. Looking for suggestions of some super simple implementation.

if I had to do something similar I would have done it this way:

public class Watcher
    {
        public readonly TimeSpan Timeout = TimeSpan.FromMinutes(10);

        private readonly FileSystemWatcher m_SystemWatcher;
        private readonly Queue<FileItem> m_Files;
        private readonly Thread m_Thread;
        private readonly object m_SyncObject = new object();
        public Watcher()
        {
            m_Files = new Queue<FileItem>();
            m_SystemWatcher = new FileSystemWatcher();
            m_SystemWatcher.Created += (sender, e) => WatcherHandler(e.FullPath);
            m_Thread = new Thread(ThreadProc)
                {
                    IsBackground = true
                };
            m_Thread.Start();
        }

        private void WatcherHandler(string fullPath)
        {
            lock (m_SyncObject)
            {
                m_Files.Enqueue(new FileItem(fullPath));
            }
        }

        private void ThreadProc()
        {
         while(true)//cancellation logic needed
         {
            FileItem item = null;
            lock (m_SyncObject)
            {
                if (m_Files.Count > 0)
                {
                    item = m_Files.Dequeue();
                }
            }

            if (item != null)
            {
                CheckAccessAndProcess(item);
            }
            else
            {
                SpinWait.SpinUntil(() => m_Files.Count > 0, 200);
            }
         }
        }

        private void CheckAccessAndProcess(FileItem item)
        {
            if (CheckAccess(item))
            {
                Process(item);
            }
            else
            {
                if (DateTime.Now - item.FirstCheck < Timeout)
                {
                    lock (m_SyncObject)
                    {
                        m_Files.Enqueue(item);
                    }
                }
            }
        }

        private bool CheckAccess(FileItem item)
        {
            if (IsFileLocked(item.Path))
            {
                if (item.FirstCheck == DateTime.MinValue)
                {
                    item.SetFirstCheckDateTime(DateTime.Now);
                }
                return false;
            }

            return true;
        }

        private void Process(FileItem item)
        {
            //Do process stuff
        }

        private bool IsFileLocked(string file)
        {
            FileStream stream = null;
            var fileInfo = new FileInfo(file);

            try
            {
                stream = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {
                return true;
            }
            finally
            {
                if (stream != null)
                    stream.Close();
            }
            return false;
        }
    }

    public class FileItem
    {
        public FileItem(string path)
        {
            Path = path;
            FirstCheck = DateTime.MinValue;
        }

        public string Path { get; private set; }
        public DateTime FirstCheck { get; private set; }

        public void SetFirstCheckDateTime(DateTime now)
        {
            FirstCheck = now;
        }
    }

From CheckAccess and IsFileLocked you may return FileStream object to ensure that file handle will be not obtained from another process between CheckAccess and Process calls;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM