System.Linq.Dynamic .Select ("new ...") не является потокобезопасным - PullRequest
4 голосов
/ 23 августа 2011

Я взял System.Linq.Dynamic.DynamicQueryable отсюда: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Проблема, с которой я сталкиваюсь, заключается в коде, который выглядит следующим образом:

var results = dataContext.GetTable<MyClass>.Select("new (MyClassID, Name, Description)").Take(5);

Похоже, что если эта строка кода выполняется несколькими потоками, находящимися рядом одновременно, динамический код Linq от Microsoft падает в их методе ClassFactory.GetDynamicClass (), который выглядит следующим образом:

    public Type GetDynamicClass(IEnumerable<DynamicProperty> properties)
    {
        rwLock.AcquireReaderLock(Timeout.Infinite);
        try
        {
            Signature signature = new Signature(properties);
            Type type;
            if (!classes.TryGetValue(signature, out type))
            {
                type = CreateDynamicClass(signature.properties);
                classes.Add(signature, type);  // <-- crashes over here!
            }
            return type;
        }
        finally
        {
            rwLock.ReleaseReaderLock();
        }
    }

Сбой - простая словарная ошибка: «Элемент с таким же ключом уже добавлен».

В Ms-коде переменная rwLock является классом ReadWriterLock, но она ничего не делает для того, чтобы блокировать попадание нескольких потоков внутрь классов. Оператор ifGetValue () if, так что ясно, Add не будет работать.

Я могу довольно легко воспроизвести эту ошибку в любом коде, который создает два или более потоков, которые пытаются выполнить оператор Select ("new").

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

Спасибо.

1 Ответ

2 голосов
/ 30 мая 2014

Я сделал следующее (требуется .NET 4 или более поздняя версия для использования System.Collections.Concurrent):

  • изменил поле classes на ConcurrentDictionary<Signature, Type>,
  • удалил всеполе ReaderWriterLock rwLock и весь код, ссылающийся на него,
  • обновлено GetDynamicClass до:

    public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) {
        var signature = new Signature(properties);
        return classes.GetOrAdd(signature, sig => CreateDynamicClass(sig.properties));
    }
    
  • удалено поле classCount иобновлено CreateDynamicClass для использования classes.Count вместо:

    Type CreateDynamicClass(DynamicProperty[] properties) {
        string typeName = "DynamicClass" + Guid.NewGuid().ToString("N");
    ...
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...