C # - блокировка в потоках со смешанным использованием файлов и папок (безопасность потоков) - PullRequest
3 голосов
/ 21 февраля 2011

Я хотел спросить, есть ли в C # или .net специальный механизм, который может делать следующее:

У меня есть многопоточное приложение, и я хочу, чтобы потоки не делали что-либо с одним файлом / папкой. как и команда lock(Object obj), она должна блокироваться, если другой поток что-то меняет в этом файле, папке или подпапке.

Например:
Все потоки начинаются в одно и то же время (хорошо, псевдо-то же время;)).
Поток A: использует folder1 \ folder2 \ file.txt и должен заблокировать его с помощью механизма.
Поток B: использует folder1 \ file2.txt и должен открыть другую блокировку
Поток C: хочет переименовать folder1 и должен быть заблокирован, пока Поток A и Поток B снимают блокировку.
Поток D: переименовывает папку 3 и открывает блокировку, которая ничего не блокирует.
Просто пример того, что я хотел бы увидеть ...

Должно быть возможным иметь неограниченные иерархические зависимости. Я знаю, что есть некоторые механизмы для поддержки блокировки файлов, и, возможно, есть что-то, что также блокирует папки с подпапками и предотвращает родительские изменения. Если ничего нет, то я должен реализовать это самостоятельно.

Ответы [ 3 ]

4 голосов
/ 21 февраля 2011

Это может звучать как магия.Но вы можете заблокировать строки.

lock (@"c:\file.txt") {
   // Do something
}

Причина в том, что .Net обрабатывает строки внутри.Обычно любые две строки, содержащие одинаковые данные, будут ссылками на один и тот же объект.В качестве альтернативы вы можете просто добавить их в словарь (ключи являются уникальными записями) и использовать значение (объект) для блокировки.

Вот простой пример:

Dictionary<string, object> LockObjects = new Dictionary<string, object>();
void DoStuff(string file) {
  // Lock while modifying LockObjects
  string lockObj = null;
  lock (LockObjects) {
    if (!LockObjects.Contains(file))
      LockObjects.Add(file, new Object());
    lockObj = LockObjects[file];
  }
  lock (lockObj) {
    // Do stuff to the file
  }
}

lock () будеттакже дождитесь завершения других, чтобы любой поток мог безопасно заблокировать и подождать.

В логике вашей программы вы ДОЛЖНЫ быть уверены, что не открыты для сценариев взаимоблокировки.Например, Thread1 блокирует File1.Тема 2 открывает Файл2.T1 не продолжится, пока не получит блокировку F2.T2 не продолжится, пока не получит блокировку на F1.Оба будут ждать вечно.Этого следует избегать при проектировании!

0 голосов
/ 21 февраля 2011

Вот мое быстрое решение проблемы.Обратная связь приветствуется.

class LockManager
{
    private static Object managerLock = new Object();

    private static Dictionary<string, int> fileLocks = new Dictionary<string, int>();
    private static Dictionary<string, int> folderLocks = new Dictionary<string, int>();



    public static void EnterFolderLock(string folderPath)
    {
        Monitor.Enter(managerLock);

        string aLocked;

        // test if all subfolders are not locked
        do
        {
            aLocked = null;

            foreach (KeyValuePair<string, int> folderLock in folderLocks)
            {
                // if it is in a locked folder
                if (folderPath.Contains(folderLock.Key))
                {
                    aLocked = folderLock.Key;
                    break;
                }
                else if (folderLock.Key.Contains(folderPath))
                {
                    aLocked = folderLock.Key;
                    break;
                }
            }


            if (aLocked == null)
            {
                foreach (KeyValuePair<string, int> fileLock in fileLocks)
                {
                    // if it has a locked file
                    if (fileLock.Key.Contains(folderPath))
                    {
                        aLocked = fileLock.Key;
                        break;
                    }
                }
            }



            if (aLocked != null)
            {
                Monitor.Exit(managerLock);

                // wait until the lock was released
                Monitor.Enter(aLocked);
                Monitor.Exit(aLocked);

                Monitor.Enter(managerLock);
            }
        } while (aLocked != null);


        if (folderLocks.ContainsKey(folderPath))
        {
            folderLocks[folderPath] = folderLocks[folderPath] + 1;
        }
        else
        {
            folderLocks.Add(folderPath, 1);
        }

        Monitor.Exit(managerLock);

        // get the file lock
        Monitor.Enter(folderPath);

    }


    public static void EnterFileLock(string filePath)
    {
        Monitor.Enter(managerLock);

        string aLockedFolder;

        // test if all subfolders are not locked
        do
        {
            aLockedFolder = null;

            foreach (KeyValuePair<string, int> folderLock in folderLocks)
            {
                // if it is in a locked folder
                if (filePath.Contains(folderLock.Key))
                {
                    aLockedFolder = folderLock.Key;
                    break;
                }
            }



            if (aLockedFolder != null)
            {
                Monitor.Exit(managerLock);

                // wait until the lock was released
                Monitor.Enter(aLockedFolder);
                Monitor.Exit(aLockedFolder);

                Monitor.Enter(managerLock);
            }
        } while (aLockedFolder != null);


        if (fileLocks.ContainsKey(filePath))
        {
            fileLocks[filePath] = fileLocks[filePath] + 1;
        }
        else
        {
            fileLocks.Add(filePath, 1);
        }

        Monitor.Exit(managerLock);

        // get the file lock
        Monitor.Enter(filePath);

    }


    public static void ExitFolderLock(string folderPath)
    {
        Monitor.Enter(managerLock);

        if (!folderLocks.ContainsKey(folderPath))
        {
            // key is missing!!!
            throw new Exception("Can't exit this folder lock if it's not open!");
        }
        else if (folderLocks[folderPath] == 0)
        {
            // key is missing!!!
            throw new Exception("Can't exit this folder lock if it's not open! Something strange happened. Please debug.");
        }
        else if (folderLocks[folderPath] == 1)
        {
            // last entry remove
            folderLocks.Remove(folderPath);
        }
        else
        {
            folderLocks[folderPath] = folderLocks[folderPath] - 1;
        }

        Monitor.Exit(folderPath);

        Monitor.Exit(managerLock);
    }


    public static void ExitFileLock(string filePath)
    {
        Monitor.Enter(managerLock);

        if (!fileLocks.ContainsKey(filePath))
        {
            // key is missing!!!
            throw new Exception("Can't exit this file lock if it's not open!");
        }
        else if (fileLocks[filePath] == 0)
        {
            // key is missing!!!
            throw new Exception("Can't exit this file lock if it's not open! Something strange happened. Please debug.");
        }
        else if (fileLocks[filePath] == 1)
        {
            // last entry remove
            fileLocks.Remove(filePath);
        }
        else
        {
            fileLocks[filePath] = fileLocks[filePath] - 1;
        }

        Monitor.Exit(filePath);

        Monitor.Exit(managerLock);
    }
}
0 голосов
/ 21 февраля 2011

Вы можете lock при открытии и закрытии файла и записи данных о "блокировке файла". Потоки, которые запускаются в заблокированный файл, могут ожидать сигнала, например:

lock(somelock)
{
  while (MyLockedFiles.Contains(thefile))
  {
    // check again as soon as a file closes
    Monitor.Wait(somelock);
  }
  // Open the file
  MyLockedFiles.Add(thefile);
}

А при закрытии файла:

lock(somelock)
{
  MyLockedFiles.Remove(thefile);
  // make blocked threads check the MyLockedFiles list
  Monitor.PulseAll(somelock);
}

Все это в классе оболочки File I / O, так что вы можете открывать файлы только в одном месте.

...