#c# #.net #parallel-processing
#c# #.net #параллельная обработка
Вопрос:
Я пытаюсь выяснить вывод этого кода:
Dictionary<int, MyRequest> request = new Dictionary<int, MyRequest>();
for (int i = 0; i < 1000; i )
{
request.Add(i, new MyRequest() { Name = i.ToString() });
}
var ids = request.Keys.ToList();
Parallel.For(0, ids.Count, (t) =>
{
var id = ids[t];
var b = request[id];
lock (b)
{
if (b.Name == 4.ToString())
{
Thread.Sleep(10000);
}
Console.WriteLine(b.Name);
}
});
Console.WriteLine("done");
Console.Read();
вывод:
789
800
875
.
.
.
4
5
6
7
done
MyRequest — это просто фиктивный класс, используемый для демонстрации (он ничего не делает, кроме хранения значений). Моя блокировка блокирует выполнение или последние 4 помещаются в их собственный поток?
Это демонстрационная версия .NET 4.0.
ОБНОВЛЕНИЕ Хорошо, я выяснил, что они были в одном потоке, но я все равно хотел бы знать, делает ли блокировка что-нибудь, чтобы заблокировать выполнение. Я не могу себе представить, что это так.
Комментарии:
1. Что? Вы говорите о последовательных значениях? Вы можете попробовать напечатать идентификатор потока рядом с номером.
2. Бесполезная придирка:
4.ToString()
на самом деле это длиннее"4"
.3. Нет, меня не волнует последовательность. Мне просто показалось странным, что выходные данные для последних 4 были согласованными, как будто все они были в одном потоке, на который повлиял таймер.
4. Это должно быть безопасно, вы можете заблокировать любой объект, на который ссылается.
Ответ №1:
Если ids
не содержит дубликатов, эта блокировка ничего не заблокирует. Но если в ids
есть дубликаты, тогда да, при блокировке может возникнуть конфликт, поскольку разные потоки борются за доступ к одному и тому же запросу.
Комментарии:
1. Даже если (и особенно если)
MyRequest
был типом значения, это ничего бы не дало.2. Если бы я использовал Parallel. Предварительный и повторный запрос. Значения и заблокированное переданное значение, блокирует ли это перечислитель?
3. @Mike_G Нет. Перечислители отличаются от объекта, на который они указывают. Блокировка указанного объекта не приведет к блокировке перечислителя.
Ответ №2:
Ваш lock
будет блокировать выполнение только в том случае, если идентификаторы выстраиваются таким образом, что вы получаете один и тот же запрос более одного раза. Поскольку каждый раз печатаются разные имена, это не должно вызывать беспокойства.
Ответ №3:
Parallel.For
использует пул потоков для обработки вашего цикла. Как только один из его потоков свободен, он присваивает его следующему элементу. Это недетерминировано, потому что вы не знаете, сколько потоков в пуле, и вы не контролируете процессорное время, выделяемое каждому потоку. Это означает, что некоторые потоки могут завершиться раньше или позже, чем вы «естественно» ожидаете.
Ваша блокировка ничего не делает. Блокировка блокирует разделы кода, которые пытаются использовать один и тот же объект. В вашем случае вы никогда не используете один и тот же объект дважды в цикле. Тот факт, что последние обработанные идентификаторы кажутся согласованными, вероятно, является чисто случайным.
Комментарии:
1. Спасибо, знак. Эта демонстрация на самом деле просто макет перечислимого, для которого я хотел бы использовать параллельную библиотеку, чтобы избежать «бутылочного горлышка» при обработке. Может показаться, что блокировка здесь ничего не делает, но она синхронизирует другие потоки в программе. Моей главной заботой было по какой-то причине заблокировать перечисление, заблокировав значение.
2. @Mike_G В этом случае он заблокирует один поток из пула, но остальные будут продолжать работать нормально, и это не приведет к блокировке всего перечисления (если только все потоки в пуле не будут заблокированы таким образом).