Использование одного объекта блокировки - безопасный вариант. Вы определяете переменные, составляющие общее состояние, и тщательно используете блокировку каждый раз, когда вы пишете и читаете эти переменные из любого потока. Если вы сделаете это, то правильность вашего приложения будет легко доказать (легко по стандартам многопоточности, что по своей сути сложно).
Чтобы минимизировать конкуренцию за блокировку, вы должны освободить ее как можно быстрее. возможно. Вам следует избегать выполнения всего, что не связано с общим состоянием, при удерживании блокировки. Например, если вы должны вызвать метод с общей переменной в качестве аргумента, сделайте снимок переменной и используйте снимок в качестве аргумента.
int snapshot;
lock (lockObj)
{
snapshot = sharedState;
}
MethodCall(snapshot);
Если вы последуете этому совету, то конкуренция за блокировку должна быть минимальным и не должно существенно влиять на производительность вашего приложения. Но если ваши тесты показывают, что за блокировку слишком много конкуренции, вы можете рассмотреть возможность введения нескольких блокировок, чтобы повысить степень детализации схемы блокировки и уменьшить конкуренцию. Имейте в виду, что это изменение значительно увеличит сложность вашего приложения. Тупики станут возможны, поэтому вы должны быть знакомы с классическими проблемами синхронизации, такими как Five Dining Philosophers , и их решениями.