DataTable и безопасность потоков - PullRequest
2 голосов
/ 19 июля 2011

Я храню DataTable в свойстве ASP .NET Cache. Операции, которые могут быть выполнены с этим DataTable:

  • привязка к элементу управления сеткой (эта сторонняя сетка внутренне управляет объектом источника данных, после обратной передачи его DataSource равен NULL, я предполагаю, что после связывания данных он больше не использует DataTable источника данных)
  • удаление строк из таблицы данных ( Row.Delete ()

Я добавил базовые блокировки чтения / записи при явной работе с этим экземпляром DataTable, но мне интересно, есть ли другие проблемы с безопасностью потоков в этом решении. Я думаю, что-то может пойти не так, когда контроль сетки находится в середине DataBinding, а другой поток удаляет строки? Если это так, как я могу синхронизировать доступ к этой таблице, чтобы не вызывать Удалить вызовы методов, когда контроль сетки является обязательным? Есть ли какая-либо комбинация событий, где я могу разместить методы AcquireWriterLock и ReleaseWriterLock?

Спасибо, Pawel

Ответы [ 2 ]

2 голосов
/ 19 июля 2011

Если вы выставляете данные через привязку данных, то забудьте об этом;Вы не можете сделать это потокобезопасным.Даже если вы как-то оберните DataView (в пользовательский ITypedList), этого не достаточно - привязка данных делает предположения о данных, в частности IList и т. Д.собираемся случайным образом изменить длину в потоке-ориентированном виде в середине итерации данных или добавить строку в потоке пользовательского интерфейса.поток через события ... но не перекрестный.

0 голосов
/ 19 июля 2011

Как люди упоминали в других ответах:

  • обычно вы кешируете только неизменяемые данные (или, по крайней мере: рассматриваете их как неизменяемые, как только они там есть). В противном случае это не кеш
  • НЕ выставляйте несколько запросов к общим данным, если кто-то когда-либо собирается их редактировать
  • Если вы выставляете данные через привязку данных, то забудьте об этом; вы не можете сделать этот потокобезопасным

Однако мне действительно нужно было кэшировать DataTable. Причины:

  • запрос, который дает результаты, действительно огромен и занимает много времени. Также велика частота обновления страницы, которая делает вызов с этим запросом. В результате двигатель ДБ становится деловым. С кэшированием я получу только 1 дБ вызов двигателя каждые 2 минуты
  • результат этого запроса отличается. Но для пользователя вполне приемлемо видеть один и тот же результат, скажем, в течение 2 минут. Поэтому хранение данных в кеше в течение 2 минут приемлемо. Также нет других проблем, таких как параллелизм, оптимистичные / пессимистичные автономные блокировки ...

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

  • это изменение должно быть применено к БД. Перед внедрением кеша, после изменения в db, приложение снова выдало огромный запрос, чтобы получить результат с небольшими отличиями
  • теперь, с кэшированием, изменения применяются к БД и применяются к кешированным DataTable. Затем эта таблица данных снова связывается с контролем данных. Преимущества: нет необходимости совершать WCF-вызов, получать данные с помощью огромного запроса + передавать данные в веб-приложение

Вот как я реализовал блокировку для этого решения:

  1. Кэшированные данные хранятся в одноэлементной оболочке:

    public class AllocationQueue
    {
        private static object tableSyncRoot = new object();
    
  2. Это единственный фрагмент кода, который модифицирует кэшированную таблицу данных:

    internal void RemoveTaskRowFromAllocationQueue(Guid queueId, Guid taskId)
    {
        var allocationQueueEntry = GetAllocationQueueEntry(queueId);
        var queueData = allocationQueueEntry.TaskIdIndexedView;
        lock(tableSyncRoot)
        {
            int rowIndex = queueData.Find(new object[] { taskId });
            queueData[rowIndex].Delete();
        }
    }
    
  3. Это единственный фрагмент кода, который предоставляет данные для привязки данных:

    public DataTable GetAllocationQueue(Guid queueId, string filter)
    {
        var allocationQueueEntry = GetAllocationQueueEntry(queueId);
        lock (tableSyncRoot)
        {
            var rows = allocationQueueEntry.Table.Select(filter);
            if (rows.Length > 0)
            {
                return rows.CopyToDataTable<DataRow>();
            }
        }
        return null;
    }
    

Потокобезопасен и работает как брелок (я прав? :)). Но это очень специфично для моих требований.

...