Многопоточные блокировки и класс монитора не работают - PullRequest
1 голос
/ 23 октября 2009

У меня есть файл, который читается и записывается. Мне нужно убедиться, что когда он будет записан, никто больше не попытается написать в него.

Я установил блокировку всей функции, которая позволяет либо читать, либо писать, но я все еще получаю такие ошибки, как Процесс не может получить доступ к файлу 'FILENAME', так как он используется другим процессом.

public static TYPE Property{

get{
    data mydata;
    Object obj = new object();
    Monitor.Enter(obj);

    // check if data has not changed
    // if it has not, just read

    using (Stream stream = File.Open(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
        //....
    }
    // else, if data changed, then need to write to  file to save the new data

    using (Stream stream = File.Open(fileLocation, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) {
        BinaryFormatter bf = new BinaryFormatter();

        try {
            bf.Serialize(stream, (data);
        }
        //DONE processing

        Monitor.Pulse(obj);
        Monitor.Exit(obj);
        return data
}

Ответы [ 3 ]

10 голосов
/ 23 октября 2009

Вы создаете новый монитор для блокировки при каждом вызове свойства. Вам нужно заблокировать тот же самый монитор , иначе нет смысла блокировать вообще.

Вы также должны просто использовать выражение "lock" - вы никогда не ждете, поэтому нет смысла пульсировать. В настоящее время, если будут сгенерированы какие-либо исключения, вы в конечном итоге «утечете» блокировку. Это обычно было бы действительно плохой проблемой, но поскольку вы все равно не используете блокировку повторно, это маскирует проблему.

Например:

private static readonly object monitor = new object();

public static TYPE Property
{
    get
    {
        lock(monitor)
        {
            // check if data has not changed
            // if it has not, just read
            using (Stream stream = File.Open(fileLocation, 
                   FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                ....
            }
            // else, if data changed, then need to write to 
            // file to save the new data
            using (Stream stream = File.Open
                       (fileLocation, FileMode.OpenOrCreate,
                        FileAccess.ReadWrite, FileShare.Read))
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(stream, data);
            }
            return data;
        }
    }
}

Кроме того, похоже, что он выполняет больше работы, чем я действительно ожидал в собственности. Вы уверены, что метод не имеет больше смысла?

4 голосов
/ 23 октября 2009

Ну, Monitor.Enter блокирует доступ любым потокам, пытающимся установить блокировку на ЖЕ объект. Каждый раз, когда вы вводите свой геттер, вы создаете новый объект, поэтому каждый вызывающий абонент получает новую блокировку, которая ничего не знает друг о друге.

Другими словами, нет блокировки.

в качестве примечания - почему вы не используете оператор блокировки? Вам все еще понадобится объект глобальной блокировки.

1 голос
/ 23 октября 2009

Причина, по которой глобальная переменная отличается от локальной, заключается в том, что LOCK фактически блокирует точку отсчета в мемориале, насколько я понимаю. Каждый раз, когда вы создаете новый объект, то есть «Object obj = new object ();», вы создаете новый объект с его собственным уникальным указателем в памяти. Поэтому, когда LOCK проверяет, заблокирована ли точка в памяти, это не так. Потому что это совершенно новая точка в памяти, и единственное, кто ее использует, это вызывающий абонент, входящий в вашу собственность. Если ваша переменная Obj объявлена ​​глобально, она всегда будет одной и той же точкой в ​​памяти, и блокировка может фактически подтвердить, что действительно эта точка памяти либо заблокирована в настоящий момент, либо может заблокировать ее самостоятельно.

Пример: (грубо, но я думаю, что это правильно)

Object obj = new object();

Теперь у вас есть точка памяти, которая выглядит примерно так:

Память -

    * obj1

теперь вы снова вводите свою собственность и снова создаете новый объект. Ваша системная память теперь выглядит примерно так ...

Память -

   * obj1
   * obj2

Во время первой поездки ваша блокировка проверяет "Obj1" в памяти. Поскольку вызывающий 1-й поездки в вашу собственность является единственным, кто использует этот экземпляр Obj, он единственный, кто когда-либо блокировал или проверял его на предмет блокировки. Потому что он смотрит на эту копию ссылки на Obj в памяти.

Во второй поездке ваша блокировка помечается как «Obj2» в памяти.

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

Специальное примечание: я не говорю, что время жизни "obj". Я просто констатирую, как это функционирует в многопоточном процессе. надеюсь, что это поможет.

...