У меня есть класс, защищенный потоком, который использует конкретный ресурс, к которому необходимо получить доступ исключительно. В моей оценке не имеет смысла, чтобы вызывающие элементы различных методов блокировали Monitor.Enter
или ожидали SemaphoreSlim
, чтобы получить доступ к этому ресурсу.Блокировка, ожидаемые, эксклюзивные методы доступа
Например, у меня есть «дорогая» асинхронная инициализация. Так как нет смысла инициализировать несколько раз, будь то из нескольких потоков или одного, несколько вызовов должны немедленно возвращаться (или даже вызывать исключение). Вместо этого нужно создать, init и , затем распространить экземпляр на несколько потоков.
ОБНОВЛЕНИЕ 1:
MyClass
использует два NamedPipes
в любом направлении. Метод InitBeforeDistribute
не является инициализацией, а правильной настройкой соединения в обоих направлениях. Не имеет смысла, чтобы труба была доступна для потоков N
, прежде чем вы установили соединение. Как только он настроен, несколько потоков могут отправлять сообщения, но только один может реально читать/записывать в поток. Приношу свои извинения за то, что он запутывает это с плохим названием примеров.
UPDATE 2:
Если InitBeforeDistribute
реализован SemaphoreSlim(1, 1)
с правильной логикой ОЖИДАНИЯ (вместо сблокированных операций метания исключения), это добавить/Square метод OK практика? Он не выбрасывает избыточное исключение (например, в InitBeforeDistribute
), будучи заблокированным?
Ниже будет хороший плохой пример:
class MyClass
{
private int m_isIniting = 0; // exclusive access "lock"
private volatile bool vm_isInited = false; // vol. because other methods will read it
public async Task InitBeforeDistribute()
{
if (Interlocked.Exchange(ref this.m_isIniting, -1) != 0)
throw new InvalidOperationException(
"Cannot init concurrently! Did you distribute before init was finished?");
try
{
if (this.vm_isInited)
return;
await Task.Delay(5000) // init asynchronously
.ConfigureAwait(false);
this.vm_isInited = true;
}
finally
{
Interlocked.Exchange(ref this.m_isConnecting, 0);
}
}
}
Некоторые пункты:
- Если есть случай, когда блокирование/ожидание доступа к замку делает совершенный смысл, то этот пример не имеет смысла (есть смысл, то есть).
- Поскольку мне нужно ждать в методе, я должен использовать что-то вроде SemaphoreSlim, если я использую «правильную» блокировку. Исходя из Семафор для приведенного выше примера позволяет мне не беспокоиться о том, что удалил класс, как только я покончу с этим. (Я всегда не любил идею утилизации элемент, используемый несколькими потоками. Это незначительные положительный, конечно.)
- Если метод вызывается часто там могут быть некоторые показатели выгоды, которые, конечно, должны быть измеренным.
Приведенный выше пример не имеет смысла в ref. в (3), так вот еще один пример:
class MyClass
{
private volatile bool vm_isInited = false; // see above example
private int m_isWorking = 0; // exclusive access "lock"
private readonly ConcurrentQueue<Tuple<int, TaskCompletionSource<int>> m_squareWork =
new ConcurrentQueue<Tuple<int, TaskCompletionSource<int>>();
public Task<int> AddSquare(int number)
{
if (!this.vm_isInited) // see above example
throw new InvalidOperationException(
"You forgot to init! Did you already distribute?");
var work = new Tuple<int, TaskCompletionSource<int>(number, new TaskCompletionSource<int>()
this.m_squareWork.Enqueue(work);
Task do = DoSquare();
return work.Item2.Task;
}
private async Task DoSquare()
{
if (Interlocked.Exchange(ref this.m_isWorking, -1) != 0)
return; // let someone else do the work for you
do
{
try
{
Tuple<int, TaskCompletionSource<int> work;
while (this.m_squareWork.TryDequeue(out work))
{
await Task.Delay(5000) // Limiting resource that can only be
.ConfigureAwait(false); // used by one thread at a time.
work.Item2.TrySetResult(work.Item1 * work.Item1);
}
}
finally
{
Interlocked.Exchange(ref this.m_isWorking, 0);
}
} while (this.m_squareWork.Count != 0 &&
Interlocked.Exchange(ref this.m_isWorking, -1) == 0)
}
}
Существуют некоторые конкретные негативные аспекты этого примера «безблокировочного», что я должен обратить внимание?
Большинство вопросов, относящихся к «незащищенному» коду на SO, обычно советуют против него, заявляя, что это для «экспертов».Редко (я мог ошибаться в этом), я вижу предложения для книг/блогов и т. Д., Которые можно вникать в них, если кто-то будет так склонен. Если есть такие ресурсы, которые я должен изучить, пожалуйста, поделитесь. Любые предложения будут высоко оценены!
У вас есть конкретный вопрос? – i3arnon
Почему вы предпочитаете бросать исключение вместо простого ожидания задачи до ее завершения? – i3arnon
Итак, чтобы убедиться, что я вас понимаю ... у вас есть дорогостоящая процедура инициализации, которая вызывается, когда первый поток обращается к ресурсу. Каков прецедент для последующих потоков, которые пытаются получить доступ к ресурсу? С вашего поста звучит так, будто вы хотите, чтобы они бросали исключения или возвращались немедленно (без получения данных?), Ни один из которых не кажется более подходящим, чтобы просто блокировать до завершения инициализации. Я что-то упускаю? Когда что-то нуждается в эксклюзивном доступе, это похоже на точный прецедент для блокировки. – Tejs