Проблема с синхронизацией - PullRequest
1 голос
/ 15 февраля 2012

У меня есть следующий сценарий в моем коде (C # .NET 4):

У меня есть логическая переменная, которая инициализируется как false:

bool b = false;

Теперь я создаю количество потоков, каждый из которых должен прочитать значение b. первый поток, читающий b - должен изменить свое значение на true (и выполнить некоторую логику ...), а остальные ничего не должны делать. Нужно ли использовать механизм синхронизации для 'b' при чтении его значения или я могу заблокировать его только при установке его значения в true? Это должно повысить мою производительность, но мне интересно, если это безопасно ..

1 Ответ

5 голосов
/ 16 февраля 2012

Итак, требования:

  • несколько потоков читают одну bool переменную
  • если поток видит значение false, он должен изменить его на true и выполнить дополнительную работу
  • если поток видит значение true, больше ничего не должно происходить
  • только один поток должен иметь возможность изменить значение и выполнить дополнительную работу

Таким образом, код будет выглядеть так:

public class Example
{
  private bool b = false;

  public void DoSomething()
  {
    if (!b)
    {
      b = true;
      DoExtraStuff();
    }
  }
}

Это, конечно, небезопасно, если есть несколько потоков, выполняющих DoSomething, и предполагается, что DoExtraStuff должен выполняться только один раз. Проблема в том, что потоки будут состязаться при чтении и записи b.

Вам действительно нужно сделать чтение и запись в b атомарными. Это можно сделать с помощью ключевого слова lock.

public class Example
{
  private bool b = false;
  private object locker = new object();

  public void DoSomething()
  {
    bool trigger = false;
    lock (locker)
    {
      if (!b)
      {
        b = true;
        trigger = true;
      }
    }

    if (trigger)
    {
      DoExtraStuff();
    }
  }
}

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

public class Example
{
  private int b = 0;

  public void DoSomething()
  {
    if (Interlocked.CompareExchange(ref b, 0, 1) == 0)
    {
      DoExtraStuff();
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...