Синхронизация потоков в C#

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

Ошибки синхронизации возникают, когда несколько потоков работают с одним источником данных, например с файлом или базой данных.

 

Продемонстрирую небольшой пример (выражаю благодарность Бондареву Владимиру за помощь). Представим, что мы пишем программу управления обувным магазином. Нам необходимо написать метод Add(), который будет добавлять на склад 1 пару обуви. Кроме того метод Add() будет проверять количество правых и левых ботинок и если оно разное, то подаст сигнал. Попробуем добавлять ботинки в несколько потоков и посмотрим, что из этого выйдет.

using System;
using System.Threading;


namespace ConsoleApplication5
{
    class Store
    {
        int left, right;

        public void Add()
        {
                if (left != right)
                    Console.WriteLine("Нехватает обуви");
                left ++;
                // Это, чтобы успел поработать и другой поток.
                Thread.Sleep(100);
                right ++;
        }
    }

    class Programm
    {
        static void Main(string[] args)
        {
            Store store = new Store();
            for (int i = 0; i < 10; ++i)
            {
                new Thread(new ThreadStart(store.Add)).Start();
            }
        }
    }
}

В методе Main() мы выполняем наш метод Add() 10 раз, в отдельных потоках. Если что-то не понятно, то ознакомтесь с предыдущей статьей.

Запустив программу, мы увидим несколько надписей "Нехватает обуви". Почему это происходит? Все дело в том, что мы добавляем левый и правый ботинок не сразу, а с небольшой задержкой (в данном случае искусственной, полученной с помощью метода Sleep(), который усыпляет поток на n милисекунд). Именно в момент этой задержки на складе левых ботинок больше чем правых. И если другой поток в это время начнет делать проверку, то на экран будет выведено сообщение "Нехватает обуви".

Разумеется, что такое поведение программы не желательно, поэтому нам необходимо синхронизовать потоки. Иными словами мы должны запретить потоку работать, пока не закончил работу другой. В языке C# для этого есть специальное средство, называется оно lock (блокировка). Нам нужно лишь заблокировать опасные места, все остальное сделает система. Для этого нам нужно создать объект блокировки, за который будут "бороться" наши потоки. У кого объект, тот и имеет право работать.

Внесем в программу небольшие изменения:
using System;
using System.Threading;


namespace ConsoleApplication5
{
    class Store
    {
        int left, right;
        object LockObj = new object();

        public void Add()
        {
            lock (LockObj)
            {
                if (left != right)
                    Console.WriteLine("Нехватает обуви");
                left ++;
                // Это, чтобы успел поработать и другой поток.
                Thread.Sleep(100);
                right ++;
            }
        }
    }

    class Programm
    {
        static void Main(string[] args)
        {
            Store store = new Store();
            for (int i = 0; i < 10; ++i)
            {
                new Thread(new ThreadStart(store.Add)).Start();
            }
        }
    }
}

Как вы видите, тело метода Add() в блок lock(){}. В качестве объекта синхронизации выступает объект LockObj. Такая программа уже не будет выдавать ложную тревогу, но зато теряется смысл многопоточности, т.к. потоки все равно не могут работать одновременно. Но необязательно в блок lock(){} помещать все тело метода, можно поместить лишь "опасные" участки, тогда выигрышь от использования потоков все же будет.

Вот и все. Как видите избежать проблем можно очень легко, главное про них не забывать. На самом деле, существуют и другие способы синхронизации потоков: мьютексы и мониторы. Если кого-то не устраивает блокировка с помощью lock(), то вы можете поискать в интернете информацию и о других способах.


 

 

 
13.12.2008

Отзывы и комментарии

 


 
Тема
Ваше имя
Почтовый адрес
Текст сообщения
Ключ защиты:
Защита от спама
 
 
 
 
10.12  .NET Reactor
15.11  n
15.11  C# ClickOnce
 
11.10  GAC и ngen
10.10  SqlTypes