Герберт Шилдт - C# 4.0: полное руководство
- Название:C# 4.0: полное руководство
- Автор:
- Жанр:
- Издательство:ООО И.Д. Вильямс
- Год:2011
- Город:Москва -- Киев
- ISBN:978-5-8459-1684-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Герберт Шилдт - C# 4.0: полное руководство краткое содержание
В этом полном руководстве по C# 4.0 - языку программирования, разработанному специально для среды .NET, - детально рассмотрены все основные средства языка: типы данных, операторы, управляющие операторы, классы, интерфейсы, методы, делегаты, индексаторы, события, указатели, обобщения, коллекции, основные библиотеки классов, средства многопоточного программирования и директивы препроцессора. Подробно описаны новые возможности C#, в том числе PLINQ, библиотека TPL, динамический тип данных, а также именованные и необязательные аргументы. Это справочное пособие снабжено массой полезных советов авторитетного автора и сотнями примеров программ с комментариями, благодаря которым они становятся понятными любому читателю независимо от уровня его подготовки.
Книга рассчитана на широкий круг читателей, интересующихся программированием на C#.Введите сюда краткую аннотацию
C# 4.0: полное руководство - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Thread.CurrentThread.Name + " равна " +
sum);
Thread.Sleep(10); // разрешить переключение задач
}
return sum;
}
}
}
class MyThread {
public Thread Thrd;
int[] a;
int answer;
// Создать один объект типа SumArray для всех
// экземпляров класса MyThread.
static SumArray sa = new SumArray();
// Сконструировать новый поток,
public MyThread(string name, int [ ] nums) {
a = nums;
Thrd = new Thread(this.Run);
Thrd.Name = name;
Thrd.Start(); // начать поток
}
// Начать выполнение нового потока,
void Run() {
Console.WriteLine(Thrd.Name + " начат.");
answer = sa.SumIt(a);
Console.WriteLine("Сумма для потока " + Thrd.Name +
" равна " + answer);
Console.WriteLine(Thrd.Name + " завершен.");
}
}
class Sync {
static void Main() {
int[] a = {1, 2, 3, 4, 5};
MyThread mt1 = new MyThread ("Потомок #1", a);
MyThread mt2 = new MyThread("Потомок #2", a);
mt1.Thrd.Join();
mt2.Thrd.Join() ;
}
}
Ниже приведен результат выполнения данной программы, хотя у вас он может оказаться несколько иным.
Потомок #1 начат.
Потомок #2 начат.
Текущая сумма для потока Потомок #1 равна 1
Текущая сумма для потока Потомок #1 равна 3
Текущая сумма для потока Потомок #1 равна 6
Текущая сумма для потока Потомок #1 равна 10
Текущая сумма для потока Потомок #1 равна 15
Сумма для потока Потомок #1 равна 15
Потомок #1 завершен.
Текущая сумма для потока Потомок #2 равна 1
Текущая сумма для потока Потомок #2 равна 3
Текущая сумма для потока Потомок #2 равна 6
Текущая сумма для потока Потомок #2 равна 10
Текущая сумма для потока Потомок #2 равна 15
Сумма для потока Потомок #2 равна 15
Потомок #2 завершен.
Как следует из приведенного выше результата, в обоих потоках правильно подсчитывается сумма, равная 15.
Рассмотрим эту программу более подробно. Сначала в ней создаются три класса. Первым из них оказывается класс SumArray
, в котором определяется метод SumIt()
, суммирующий элементы целочисленного массива. Вторым создается класс MyThread
, в котором используется статический объект sa типа SumArray
. Следовательно, единственный объект типа SumArray
используется всеми объектами типа MyThread
. С помощью этого объекта получается сумма элементов целочисленного массива. Обратите внимание на то, что текущая сумма запоминается в поле sum объекта типа SumArray
. Поэтому если метод SumIt()
используется параллельно в двух потоках, то оба потока попытаются обратиться к полю sum, чтобы сохранить в нем текущую сумму. А поскольку это может привести к ошибкам, то доступ к методу SumIt()
должен быть синхронизирован. И наконец, в третьем классе, Sync
, создаются два потока, в которых подсчитывается сумма элементов целочисленного массива.
Оператор lock
в методе SumIt()
препятствует одновременному использованию данного метода в разных потоках. Обратите внимание на то, что в операторе lock
объект lockOn
используется в качестве синхронизируемого. Это закрытый объект, предназначенный исключительно для синхронизации. Метод Sleep()
намеренно вызывается для того, чтобы произошло переключение задач, хотя в данном случае это невозможно. Код в методе SumIt()
заблокирован, и поэтому он может быть одновременно использован только в одном потоке. Таким образом, когда начинает выполняться второй порожденный поток, он не сможет войти в метод SumIt()
до тех пор, пока из него не выйдет первый порожденный поток. Благодаря этому гарантируется получение правильного результата.
Для того чтобы полностью уяснить принцип действия блокировки, попробуйте удалить из рассматриваемой здесь программы тело метода SumIt()
. В итоге метод SumIt()
перестанет быть синхронизированным, а следовательно, он может параллельно использоваться в любом числе потоков для одного и того же объекта. Поскольку текущая сумма сохраняется в поле sum, она может быть изменена в каждом потоке, вызывающем метод SumIt()
. Это означает, что если два потока одновременно вызывают метод SumIt()
для одного и того же объекта, то конечный результат получается неверным, поскольку содержимое поля sum отражает смешанный результат суммирования в обоих потоках. В качестве примера ниже приведен результат выполнения рассматриваемой здесь программы после снятия блокировки с метода SumIt()
.
Потомок #1 начат.
Потомок #2 начат.
Текущая сумма для потока Потомок #1 равна 1
Текущая сумма для потока Потомок #2 равна 1
Текущая сумма для потока Потомок #1 равна 3
Текущая сумма для потока Потомок #2 равна 5
Текущая сумма для потока Потомок #2 равна 11
Текущая сумма для потока Потомок #1 равна 8
Текущая сумма для потока Потомок #1 равна 15
Текущая сумма для потока Потомок #2 равна 19
Текущая сумма для потока Потомок #1 равна 24
Текущая сумма для потока Потомок #2 равна 29
Сумма для потока Потомок #1 равна 29
Потомок #1 завершен.
Сумма для потока Потомок #2 равна 29
Потомок #2 завершен.
Как следует из приведенного выше результата, в обоих порожденных потоках метод SumIt() используется одновременно для одного и того же объекта, а это приводит к искажению значения в поле sum.
Ниже подведены краткие итоги использования блокировки.
• Если блокировка любого заданного объекта получена в одном потоке, то после блокировки объекта она не может быть получена в другом потоке.
• Остальным потокам, пытающимся получить блокировку того же самого объекта, придется ждать до тех пор, пока объект не окажется в разблокированном состоянии.
• Когда поток выходит из заблокированного фрагмента кода, соответствующий объект разблокируется.
Несмотря на всю простоту и эффективность блокировки кода метода, как показано в приведенном выше примере, такое средство синхронизации оказывается пригодным далеко не всегда. Допустим, что требуется синхронизировать доступ к методу класса, который был создан кем-то другим и сам не синхронизирован. Подобная ситуация вполне возможна при использовании чужого класса, исходный код которого недоступен. В этом случае оператор lock
нельзя ввести в соответствующий метод чужого класса. Как же тогда синхронизировать объект такого класса? К счастью, этот вопрос разрешается довольно просто: доступ к объекту может быть заблокирован из внешнего кода по отношению к данному объекту, для чего достаточно указать этот объект в операторе lock
. В качестве примера ниже приведен другой вариант реализации предыдущей программы. Обратите внимание на то, что код в методе SumIt()
уже не является заблокированным, а объект lockOn
больше не объявляется. Вместо этого вызовы метода SumIt()
блокируются в классе MyThread
.
Интервал:
Закладка: