Простой вопрос, блокирующий доступ к общему ресурсу или ко всей функции - PullRequest
3 голосов
/ 06 февраля 2011

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

Если у меня есть общий код:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
    lock(objSync)
      a += 2;

    lock(objSync)
      a += 3;
    Console.WriteLine(a.ToString());
    a=0;
    }

Я не могу ожидать, что 'a' будет равно 5 в концепотому что первый поток получает блокировку, устанавливает 'a' в 2, а затем следующий поток может получить блокировку, установив ее в '4', прежде чем первый поток сможет добавить 3, и в итоге вы получите 7 в конце.

Как я понимаю, решение состоит в том, чтобы установить блокировку вокруг всего, и тогда вы всегда можете ожидать 5. Теперь мой вопрос заключается в том, что, если между двумя блокировками будет миллион строк кода.Я не могу себе представить, чтобы поставить блокировку вокруг миллиона строк кода.Как бы вы обеспечивали безопасность потоков, но не отказывались от производительности, блокируя миллион и две строки кода?

РЕДАКТИРОВАТЬ:

Это бессмысленный код, который я написал для вопроса.Фактическое применение - система контроля линии.Есть два экрана, которые показывают текущую строку, один для клерков и один для публики.Экран для клерка принимает «щелчки» через последовательный порт, который последовательно продвигается по линии, а затем обновляет общедоступный экран с помощью события уведомления (см. Шаблон проектирования наблюдателя).Проблема в том, что они не синхронизируются, если вы спамите кликер.Я представляю, что происходит, когда первый экран добавляет номер строки, отображает его, а затем обновляет открытый экран, но перед тем, как на открытом экране появится возможность показать номер строки из базы данных, клерк снова щелкает, и номер выходитсинхронизация.«A» выше представляет значение, которое я извлекаю из базы данных, а не просто int.Есть несколько мест, где я могу изменить значение, и мне нужно, чтобы все это происходило атомарно.

Ответы [ 2 ]

4 голосов
/ 06 февраля 2011

Все зависит от того, что вы определяете как «успех».Если это счетчик, это все равно может быть правильным результатом.Если вы хотите обновить его, только если это то, что вы думали , сделайте снимок внутри первой блокировки и аналогично сравните его со снимком в последней блокировке.В этом сценарии вы можете заменить большую часть кода на Interlocked.CompareExchange.Аналогично в сценарии «счетчик» Interlocked.Increment является вашим другом.

Если код должен соответствовать, тогда у вас есть несколько вариантов:

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

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

И кстати;окончательное присвоение 0 должно, вероятно, также быть частью той же стратегии блокировки.Блокировки работают, только если их учитывает весь доступ.

Но только ваш сценарий может сказать нам, каково «правильное» поведение в конфликте.

0 голосов
/ 06 февраля 2011

Это вопрос дизайна на самом деле.Если ваш метод вычисляет различные значения для «a», в зависимости от определенных обстоятельств, но вы не хотите, чтобы другой поток изменял значение «a», когда операция активна, что-то вроде этого более уместно:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
      int b;
      lock(objSync)
        b = a;
      b += 2;

      //million lines of code

      b +=3;
      lock(objSync)
      {
        a = b;
        Console.WriteLine(a.ToString());
        a=0;
      }
    }
...