- •А.А. Волосевич
- •Содержание
- •1. Работа с числами
- •2. Представление даты и времени
- •3. Работа со строками и текстом
- •4. Преобразование информации
- •5. Сравнение для выяснения равенства
- •6. Сравнение для выяснения порядка
- •7. Жизненный цикл объектов
- •7.1. Алгоритм сборки мусора
- •7.2. Финализаторы и интерфейс iDisposable
- •7.3. Слабые ссылки
- •8. Перечислители и итераторы
- •9. Стандартные интерфейсы коллекций
- •10. Массивы и класс System.Array
- •11. Типы для работы с коллекциями-списками
- •12. Типы для работы с коллекциями-множествами
- •13. Типы для работы с коллекциями-словарями
- •14. Типы для создания пользовательских коллекций
- •15. Технология linq to Objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •16. Работа с объектами файловой системы
- •17. Ввод и вывод информации
- •17.1. Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •17.2. Адаптеры потоков
- •18. Основы xml
- •19. Технология linq to xml
- •20. Дополнительные возможности обработки xml
- •21. Сериализация времени выполнения
- •22. Контракты данных и xml-сериализация
- •23. Состав и взаимодействие сборок
- •24. Метаданные и получение информации о типах
- •25. Позднее связывание и кодогенерация
- •26. Атрибуты
- •27. Динамическое связывание
- •28. Файлы конфигурации
- •29. Диагностика и мониторинг
- •30. Процессы и домены
- •31. Основы многопоточного программирования
- •32. Синхронизация потоков
- •32.1. Критические секции
- •32.2. Синхронизация на основе подачи сигналов
- •32.3. Неблокирующие средства синхронизации
- •32.4. Разделение данных между потоками
- •33. Библиотека параллельных задач
- •33.1. Параллелизм на уровне задач
- •33.2. Параллелизм при императивной обработке данных
- •33.3. Параллелизм при декларативной обработке данных
- •33.4. Обработка исключений и отмена выполнения задач
- •33.5. Коллекции, поддерживающие параллелизм
- •34. Асинхронный вызов методов
- •Литература
32.4. Разделение данных между потоками
Если некий метод запускается в нескольких потоках, только локальные переменные метода будут уникальными для потока. Поля объектов по умолчанию разделяются между всеми потоками. В пространстве имён System определён атрибут [ThreadStatic], применяемый к статическим полям. Если поле помечено таким атрибутом, то каждый поток будет содержать свой экземпляр поля. Для [ThreadStatic]-полей не рекомендуется делать инициализацию при объявлении, так как код инициализации выполнится только в одном потоке.
public class SomeClass
{
public static int SharedField = 25;
[ThreadStatic]
public static int NonSharedField;
}
Для создания неразделяемых статических полей можно использовать тип ThreadLocal<T>. Перегруженный конструктор ThreadLocal<T> принимает функцию инициализации поля. Значение поля хранится в свойстве Value.
public class Slot
{
private static readonly Random rnd = new Random();
private static int Shared = 25;
private static ThreadLocal<int> NonShared =
new ThreadLocal<int>(() => rnd.Next(1, 20));
public static void PrintData()
{
Console.WriteLine("Thread: {0} Shared: {1} NonShared: {2}",
Thread.CurrentThread.Name,
Shared, NonShared.Value);
}
}
public class MainClass
{
public static void Main()
{
// для тестирования запускаем три потока
new Thread(Slot.PrintData) {Name = "First"}.Start();
new Thread(Slot.PrintData) {Name = "Second"}.Start();
new Thread(Slot.PrintData) {Name = "Third"}.Start();
Console.ReadLine();
}
}
Отметим, что класс Thread имеет статические методы AllocateDataSlot(), AllocateNamedDataSlot(), GetNamedDataSlot(), FreeNamedDataSlot(), GetData(), SetData(), которые предназначены для работы с локальными хранилищами данных потока. Эти локальные хранилища могут рассматриваться как альтернатива неразделяемым статическим полям.
Распространённый шаблон при разработке многопоточных приложений – неизменяемый объект (immutable object). После создания такой объект допускает только чтение своих полей, но не запись. Приведём пример класса, объекты которого являются неизменяемыми:
public class ProgressStatus
{
public readonly int PercentComplete;
public readonly string StatusMessage;
public ProgressStatus(int percentComplete, string statusMessage)
{
PercentComplete = percentComplete;
StatusMessage = statusMessage;
}
}
Достоинство неизменяемых объектов с точки зрения многопоточности заключается в том, что работа с ними требует коротких блокировок, обычно обрамляющих операции присваивания объектов:
public class WorkWithImmutable
{
private readonly object _locker = new object();
private ProgressStatus _status;
public void SetFields()
{
// создаём и настраиваем временный объект
var status = new ProgressStatus(50, "Working on it");
// переносим информацию, используя короткую блокировку
lock (_locker) _status = status;
}
public void ReadInfo()
{
// используя короткую блокировку, создаём временную копию
ProgressStatus statusCopy;
lock (_locker) statusCopy = _status;
// работаем с копией
int pc = statusCopy.PercentComplete;
string msg = statusCopy.StatusMessage;
}
}