Почему блокировка на публичном объекте - плохая идея
Хорошо, я использовал блокировки совсем немного, но у меня никогда не было такого сценария раньше. У меня есть два разных класса, которые содержат код, используемый для изменения одной и той же базы данных MSAccess:
public class DatabaseNinja
{
public void UseSQLKatana
{
//Code to execute queries against db.TableAwesome
}
}
public class DatabasePirate
{
public void UseSQLCutlass
{
//Code to execute queries against db.TableAwesome
}
}
Это проблема, потому что транзакции в базе данных не могут выполняться параллельно, и эти методы (UseSQLKatana и UseSQLCutlass) вызывают разные потоки.
В моих исследованиях я вижу, что использование плохой практики для использования публичного объекта как объекта блокировки, так как я могу заблокировать эти методы, чтобы они не запускались в тандеме? Является ли ответ просто иметь эти методы в одном классе? (Это действительно не так просто в моем реальном коде)
Ответы
Ответ 1
Ну, во-первых, вы можете создать третий класс:
internal class ImplementationDetail
{
private static readonly object lockme = new object();
public static void DoDatabaseQuery(whatever)
{
lock(lockme)
ReallyDoQuery(whatever);
}
}
а теперь UseSQLKatana и UseSQLCutlass вызывают реализациюDetail.DoDatabaseQuery.
Во-вторых, вы можете решить не беспокоиться об этом и заблокировать объект, видимый для обоих типов. Основная причина, почему этого избежать, состоит в том, что становится трудно рассуждать о том, кто блокирует объект, и трудно защитить от враждебного частично доверенного кода, злонамеренно блокирующего объект. Если вы не заботитесь ни о нижнем, то вам не нужно слепо следовать рекомендациям.
Ответ 2
Причиной плохой практики блокировки публичного объекта является то, что вы никогда не можете быть уверены, кто ELSE блокирует этот объект. Хотя маловероятно, кто-то еще когда-нибудь сможет решить, что они хотят захватить ваш объект блокировки, и сделать какой-то процесс, который в конечном итоге вызывает ваш код, где вы блокируете этот же объект блокировки, и теперь у вас есть невозможный тупик, чтобы понять. (Это та же проблема для использования 'this').
Лучшим способом сделать это будет использование общедоступного объекта Mutex. Они намного более тяжелые, но гораздо легче отладить проблему.
Ответ 3
Используйте Mutex
.
Вы можете создать мьютекс в основном классе и вызвать метод Wait в начале каждого класса (метода); затем установите mutex, поэтому, когда другой метод называется, он ждет завершения первого класса.
Ах, не забудьте освободить мьютекс, выходящий из этих методов...
Ответ 4
Вы можете использовать публичный объект LOCK в качестве объекта блокировки. Вам просто нужно указать, что создаваемый объект является объектом Lock, который используется исключительно для блокировки класса Ninja и Pirate.
Ответ 5
Здесь я вижу два разных вопроса:
Почему это плохая идея для lock
для общедоступного объекта?
Идея состоит в том, что блокировка объекта ограничивает доступ, пока поддерживается lock
- это означает, что ни один из его членов не может быть доступен, а другие источники могут не знать о lock
и пытаться использовать экземпляр, даже пытаясь приобрести сам lock
, что вызывает проблемы.
По этой причине используйте специальный экземпляр объекта для блокировки.
Как заблокировать эти методы, чтобы они не запускались в тандеме?
Вы можете рассмотреть класс Mutex
; создание "глобального" мьютекса позволит вашим классам работать на основе знания состояния блокировки во всем приложении. Или вы можете использовать общий ReaderWriterLockSlim
экземпляр, но я бы не рекомендовал его совместное использование.