Когда использовать блокировку потока в C #? - PullRequest
27 голосов
/ 04 января 2012

У меня есть сервер, который обрабатывает несколько входящих подключений к сокету и создает 2 разных потока, которые хранят данные в формате XML.

Я использовал оператор lock для обеспечения безопасности потока почти в каждом обработчике событий, вызываемом асинхроннои в 2 темы в разных частях кода.К сожалению, при таком подходе мое приложение значительно замедляется.

Я пытался вообще не использовать lock, и сервер очень быстр в выполнении, даже хранилище файлов, кажется, увеличивается;но программа вылетает по причинам, которые я не понимаю, через 30 секунд - 1 минута.работы.

Итак.Я думал, что лучший способ - использовать меньше замков или использовать их там, где это строго необходимо.Таким образом, у меня есть 2 вопроса:

  1. Требуется ли блокировка, когда я пишу только в общедоступные переменные (списки C #) или даже когда я читаю из них?

  2. Требуется ли блокировка только в асинхронных потоках, созданных обработчиком сокета или в других местах?

Кто-то может дать мне несколько практических рекомендаций о том, какработать.Я не буду публиковать весь код на этот раз.Нет смысла размещать около 2500 строк кода.

Ответы [ 5 ]

87 голосов
/ 04 января 2012

Вы когда-нибудь сидели в своей машине или в автобусе на красный свет, когда нет перекрестка?Большая трата времени, верно?Замок похож на идеальный светофор.Он всегда зеленый, за исключением случаев, когда на перекрестке есть движение.

Ваш вопрос: «Я слишком много времени провожу в пробках в ожидании на красный свет. Должен ли я просто включить красный свет? Или даже лучше, я должен удалитьгорит полностью и просто позволяет каждому проехать перекресток на скоростях шоссе без каких-либо средств управления перекрестком? "

Если у вас проблемы с производительностью с замками, то удаление замков - это последняя вещь, которую выследует сделать. Вы ждете на этом красном сигнале именно потому, что на перекрестке происходит перекрестное движение.Замки являются чрезвычайно быстрыми , если они не оспариваются.

Вы не можете устранить свет, не устранив сначала перекрестное движение.Поэтому наилучшим решением является устранение перекрестного трафика .Если замок никогда не оспаривается, вы никогда не будете его ждать.Выясните, почему перекресток тратит так много времени на перекрестке;не убирай свет и надейся, что столкновений нет.Будет.

Если вы не можете этого сделать, тогда добавление более мелкозернистых блокировок иногда помогает .То есть, может быть, у вас есть все дороги в городе, сходящиеся на одном перекрестке.Возможно, вы можете разделить это на два пересечения, чтобы код мог проходить через два разных пересечения одновременно.

Обратите внимание, что делает автомобили быстрее (получая более быстрый процессор) или сокращение дорог (устранение длины пути кода) часто усугубляет проблему в многопоточных сценариях.Так же, как в реальной жизни;если проблема в тупике, то покупка более быстрых автомобилей и езда по более коротким дорогам приведет их к пробке быстрее, но не быстрее.

33 голосов
/ 04 января 2012

Требуется ли блокировка при записи только в общедоступные переменные (списки C #) или даже при чтении из них?

Да (даже когда вы читаете).

Требуется ли блокировка только в асинхронных потоках, созданных обработчиком сокета или в других местах?

Да. Везде, где код обращается к разделу кода, который является общим, всегда блокируйте.


Звучит так, что вы можете не блокировать отдельные объекты, а блокировать одну вещь для всех ситуаций блокировки.

Если это так, вставьте умные дискретные блокировки, создав индивидуальные уникальные объекты, которые связаны и блокируют только определенные разделы одновременно, которые не мешают другим потокам в других разделах.

Вот пример:

// This class simulates the use of two different thread safe resources and how to lock them
// for thread safety but not block other threads getting different resources.
public class SmartLocking
{
    private string StrResource1 { get; set; }
    private string StrResource2 { get; set; }

    private object _Lock1 = new object();
    private object _Lock2 = new object();

    public void DoWorkOn1( string change )
    {
        lock (_Lock1)
        {
            _Resource1 = change;
        }
    }

    public void DoWorkOn2( string change2 )
    {
        lock (_Lock2)
        {
            _Resource2 = change2;
        }
    }
}
2 голосов
/ 04 января 2012

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

Если вы хотите выполнить итерацию коллекции, скопируйте все элементы в новую коллекцию и затем выполните итерацию копии. * Т.е. 1003 *

var newcollection; // Initialize etc.
lock(mycollection)
{
  // Copy from mycollection to newcollection
}

foreach(var item in newcollection)
{
  // Do stuff
}

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

1 голос
/ 04 января 2012

Причина, по которой вам нужно заблокировать во время чтения:

Допустим, вы вносите изменения в одно свойство, и оно читается дважды, пока поток находится между блокировками.Однажды прямо перед тем, как мы внесем какое-либо изменение, а затем еще одно, мы получим противоречивые результаты.

Надеюсь, это поможет,

0 голосов
/ 04 января 2012

В основном на это можно ответить довольно просто:

Вам необходимо заблокировать все объекты, к которым обращаются разные потоки. Это действительно не имеет значения, если речь идет о чтении или письме. Если вы читаете, и другой поток перезаписывает данные одновременно, считанные данные могут стать недействительными, и вы, возможно, выполняете недопустимые операции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...