Опубликован: 11.09.2006 | Уровень: специалист | Доступ: свободно
Лекция 7:

Асинхронное программирование

Меры безопасности в многопоточных приложениях

При использовании асинхронной модели программирования не исключена ситуация, когда два или более потоков начнут использовать один объект, что может привести к непредвиденным обстоятельствам и ошибкам. Пространство имен System.Threading включает в себя классы для управления синхронизацией доступа и обеспечения потоковой безопасности.

Самые распространенные проблемы, возникающие при использовании многопоточной модели программирования, — это "Гонка" (Race condition) и "Мертвая точка" (Deadlock).

Проблема "Гонки" встречается, когда два или более потоков начинают использовать одновременно один объект. Например, в то время как один поток считывает данные из структуры, другой может начать изменять ее. После выполнения этих операций неизвестно, какие данные считал первый поток: старые, новые или перемешанные.

Проблема "Мертвой точки" возникает при синхронизации в многопоточных приложениях, когда два потока ожидают выполнения или освобождения ресурсов друг друга. К примеру, два потока переводят деньги со счета А в счет В и обратно. Первый поток запрограммирован сделать следующее:

  1. ожидать освобождения и заблокировать счет А;
  2. ожидать освобождения и заблокировать счет В;
  3. перевести деньги;
  4. разблокировать потоки.

Второй поток выполняет те же действия, за исключением того, что сперва блокируется счет В, а затем А. Итак, первый поток заблокировал счет А, а в это время второй поток заблокировал счет В. После этого каждый поток будет ждать, пока другой не освободит ресурсы, но этого не произойдет.

Одним из решений этих проблем является закрытие общего доступа к объектам между потоками. Но иногда приходится использовать одни переменные в многопоточных приложениях. Для таких целей в пространстве имен System.Threading находятся классы, помогающие осуществить синхронизацию объектов.

Автоматическая синхронизация

Библиотека .NET Framework предоставляет автоматическую синхронизацию объектов. Для этого служит атрибут SynchronizationAttribute, находящийся в пространстве имен System.Runtime.Remoting.Contexts. Его можно установить на любой класс, унаследованный от класса ContextBoundObject. При этом все свойства и методы этого класса будут синхронизированы, т.е. только один поток сможет работать с объектом этого класса. Для использования объектов внутри этого класса несколькими потоками следует их сделать статическими, т. е. установить свойство static. Пример использования этого атрибута:

using System;
using System.Runtime.Remoting.Contexts;

namespace AutomaticSynchronization
{	
	[Synchronization()]
	class MyClass:ContextBoundObject
	{
		string myName;
		/// <summary>
		/// Конструктор
		/// </summary>
		/// <param name="name">Имя.</param>
		public MyClass(string name)
		{
			this.myName = name;
		}
		/// <summary>
		/// Параллельные потоки НЕ могут 
		/// одновременно использовать свойство Name
		/// </summary>
		public string Name
		{
			get { return myName;}
			set { myName = value;}
		}
		static int myAge;
		/// <summary>
		/// Статический конструктор.
		/// </summary>
		static MyClass()
		{
			myAge = 15;
		}
		/// <summary>
		/// Возраст. Статическое свойство. Параллельные потоки могут 
		/// одновременно использовать это свойство.
		/// </summary>
		public static int Age
		{
			get{ return myAge;}
			set{ myAge = value;}
		}
	}
}
Листинг 7.12.
Использование областей синхронизации

Следующим способом синхронизации является использование ключевого слова lock. Его использует класс Monitor из пространства имен System.Threading для блокировки области кода. Это ключевое слово может применяться и в статических методах. Оно имеет следующий синтаксис:

lock(объект) блок выражений.

Пример использования ключевого слова lock:

public void Function()
{
    System.Object lockThis = new System.Object();
    lock(lockThis)
    {
// Код, который нуждается в эксклюзивных правах
    }
}

В процессе компиляции приложения среда заменяет выражение lock на блок try, finally:

SomeClass x = new SomeClass();
lock(x)
{
    // Выполнение действий.
    DoSomething();
}

   	// Эквивалентный код:

SomeClass x = new SomeClass();
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    // Выполнение действий.
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}

В статических методах lock можно включать прямо внутри свойств:

using System;
using System.Threading;

class Cache
{
public static void Add(object x)
{
// Код, который не нуждается в эксклюзивных правах
lock (typeof(Cache))
{
// Код, который нуждается в эксклюзивных правах
}
// Код, который не нуждается в эксклюзивных правах
}
}
Ручная синхронизация

Библиотека .NET Framework предлагает еще один способ синхронизации — применение атрибута MethodImplAttribute, принадлежащего пространству имен System.Runtime.CompilerServices. В отличие от ключевого слова lock или класса Monitor, действие этого атрибута распространяется не на отдельный кусок кода, а на весь метод. Его можно использовать как с методами объекта, так и со статическими методами. Например, класс SomeClass имеет два метода с синхронизацией доступа, один из который статический:

using System;
using System.Threading;
using System.Runtime.CompilerServices;

namespace Sample
{
	
	/// <summary>
	/// Применение  атрибута MethodImplAttribute
	/// </summary>
	public class SomeClass
	{
		/// <summary>
		/// Статическая переменная.
		/// </summary>
		static int staticValue = 0;
		/// <summary>
		/// Переменная экземпляра.
		/// </summary>
		int instanceValue = 0;
		/// <summary>
		/// Синхронизированный доступ к переменной экземпляра.
		/// </summary>
		[MethodImplAttribute(MethodImplOptions.Synchronized)]
		public void ChangeInstance()
		{
			// Действия метода.
		}
		/// <summary>
		/// Синхронизированный доступ к статической переменной
		/// </summary>
		[MethodImplAttribute(MethodImplOptions.Synchronized)]
		public static void ChangeStatic()
		{
			// Действия метода.
		}
	}
}
Листинг 7.13.

В приведенном примере с web-службой используется техника callback для завершения вызова метода HelloWorld. Обратите внимание, что web-служба автоматически создает асинхронные варианты всех методов, помеченных атрибутом [WebMethod].

Елена Дьяконова
Елена Дьяконова

При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: 

Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll

Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан.

Затем:

Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll

Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз.

Александр Сороколет
Александр Сороколет

Свойство WindowState формы blank Maximized. Не открывается почемуто на всё окно, а вот если последующую форму бланк открыть уже на макс открывается :-/