Связывание блокировка/мьютекс с данными, которые он защищает


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

Представьте себе разработчика было написано следующее (пример) объект кэширования, но забыл взять _imageLock при очистке кэша.

Используя традиционные .механизм чистый замок (только замок на объекте) мы могли бы развратить словарь если два потока вызвать Add() и команды clearcache() в то же время.

Используя эти классы, замок и защищенбудет вместо того, чтобы бросать исключение в метод clearcache (), потому что защищен объекта осуществляется за пределами его замка. Это исключение должно поверхности во время разработчики обычную процедуру проверки, прежде чем код, даже зарегистрировался.

public class SomeThreadSafeCache
{
    Padlock _imageLock = new Padlock();
    Protected<Dictionary<string, byte[]>> _imageCache;

    public SomeThreadSafeCache()
    {
        //The variable _imageCache is associate with the _imageLock Padlock
        //i.e. _imageCache is only available when we're locking on _imageLock.
        _imageCache = new Protected<Dictionary<string, byte[]>>(
            new Dictionary<string, byte[]>(),
            _imageLock);
    }

    public void AddImage(string key, byte[] data)
    {
        using (_imageLock.Lock())
        {
            _imageCache.Value.Add(key, data);
        }
    }

    public void ClearCache()
    {
        //Throws an exception because the protected
        //resource is accessed outside of it's lock.
        _imageCache.Value.Clear();
    }
}

(Я знаю, что .net теперь поставляется с параллельными коллекциями, которые делают мой пример бессмысленно, но общая идея, связывающая ресурс с замком-это то, что я пытаюсь сделать в)

Защищенные

public class SomeOtherThreadSafeObject
{
    Padlock _lock1 = new Padlock();
    Padlock _lock2 = new Padlock();

    Protected<int> _i;

    public SomeOtherThreadSafeObject()
    {
        _i = new Protected<int>(0, _lock1, _lock2);
    }

    public void IncrementI()
    {
        using (_lock1.Lock())
        {
            using (_lock2.Lock())
            {
                _i.Value++;
            }
        }
    }
}

(Банальный пример для наглядности только, сблокированного.Функция приращения бы сделать лучше.)

Если _lock1 или _lock2 не забрали тогда

_i.Value++   

будет бросать исключение, заявив, что объект не полностью заблокирована.

Соблюдение порядка, в котором замки берутся, выходят за рамки программы занятий. Я также пытаюсь избежать старый интерфейс ICollection.Шаблон свойства syncroot, не позволяя

Protected<T>.Padlock.Lock() 

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

Я прошу пересмотреть общий подход/идея. - Это похоже на разумную систему? Есть ли какие-то явные проблемы с этим, что я не подумал? Если кто-то хочет увидеть фактический код реализации, то я могу разместить его на GitHub (или где-то похожие).

(Я понимаю, что это нарушения метод IDisposable/используя рисунок, но для дополнительной безопасности потоков, я думаю, это будет стоит для нас).



Комментарии
4 ответа

Я признаюсь, что я думаю, что ваш подход выглядит болезненно. Как видно из других ответов, это недостаточно, чтобы защитить ваши данные вне контейнера класс. И есть путаница в вашем примере кода, является ли защищенный контейнер или ThreadSafeCache-это контейнер. Я думаю, что нужно пересмотреть принцип инкапсуляции. Когда у вас есть коллекция, которая требует многопоточной обновления и доступа, необходимо защитить коллекцию С класс-оболочку. Оболочка инкапсулирует базовой коллекции в полном объеме. Думаю, что "частная". Если вы не хотите открывать замок, то вы не подвергайте итератор/перечислитель. Скорее всего, вы делаете что-то похожее на метод forEach списка:

public void ForEach(Action<T> t){
lock(_items) foreach(var item in _items) t.Invoke(item);
}

2
ответ дан 27 июня 2012 в 04:06 Источник Поделиться

Это выглядит многообещающе, хотя и без возможности исполнять заказ, я бы удалить возможность связать несколько замков с защищенным объектом, поскольку вы не просто просит в тупик.

Также, как вы защищаете против чего-то вроде этого:

Protected<Reference> _i = new Protected<Reference>(0, lock);
Reference unprotected_i;
using (lock.Lock())
{
unprotected_i = _i.Value;
}
unprotected_i.boom();// any modifying operation.

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

0
ответ дан 7 июля 2011 в 03:07 Источник Поделиться

Я не уверен, что понял вопрос. Каковы эти классы, собирался дать мне, что стандартная блокировка не будет?

Я бы написал, что первый класс что-то вроде этого:

public sealed class SomeThreadSafeCache
{
static readonly object l = new object();
static volatile SomeThreadSafeCache instance;

readonly Dictionary<string, byte[]> _imageCache;

SomeThreadSafeCache()
{
_imageCache = new Dictionary<string, byte[]>();
}

public static SomeThreadSafeCache Instance
{
get
{
if (instance == null)
{
lock (l)
{
if (instance == null)
instance = new SomeThreadSafeCache();
}
}

return instance;
}
}

public void AddImage(string key, byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
if (key == null)
throw new ArgumentNullException("key");

var v = (byte[]) data.Clone();
lock (l)
{
_imageCache.Add(key, );
}
}

public byte[] GetImage(string key)
{
if (key == null)
throw new ArgumentNullException("key");

byte[] v;
lock (l)
{
_imageCache.TryGetValue(key, out v);
}
if (v == null)
{
return null;
}
return (byte[]) v.Clone();
}

public void ClearCache()
{
lock(l)
{
_imageCache.Clear();
}
}
}

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

Я думаю, что статический конструктор вполне подойдет для шаблона Singleton, но я не 100% уверен, что это охватывает все вопросы потокобезопасности. Я не понимаю, как хоть один объект блокировки не будет.

0
ответ дан 3 мая 2012 в 01:05 Источник Поделиться

Есть ли основания для создания отрасли "исключение" для доступа к "ценности"? Может быть, лучше использовать что-то вроде

using(var lockedDictionary = _imageCache.Lock())
{
lockedDictionary.Value.Add(key, data);
}

0
ответ дан 24 декабря 2012 в 11:12 Источник Поделиться