Как заблокировать переменную, используемую в нескольких потоках

Я задал здесь вопрос плохо Заблокировать переменную в нескольких потоках, поэтому для ясности я собираюсь спросить об этом здесь и надеюсь, что смогу запросить ее правильно.

classA
  creates instance of classB
  has methodA that can update & uses classB myVar
  has methodB that can update & uses classB myVar

classB
  has a variable myVar

methodA и methodB могут запускаться в отдельных потоках (называемых в новых потоках из основного). Как обеспечить безопасность потока?

Ответ 1

Используйте ключевое слово lock для защиты кода, который может выполняться одновременно несколькими потоками.

public class ClassA
{
  private ClassB b = new ClassB();

  public void MethodA()
  {
    lock (b)
    {
      // Do anything you want with b here.
    }
  }

  public void MethodB()
  {
    lock (b)
    {
      // Do anything you want with b here.
    }
  }
}

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

Ответ 3

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

То, что кажется отсутствующим, заключается в том, должен ли classA.methodA или classA.methodB завершить свою работу с classB.myVar перед другим потоком, вызывающим классA.methodA(...) или classA.methodB(...), Он определит, какой тип блокировки вам понадобится.

Например, если вам нужна гарантия на чтение значения, это будет выглядеть следующим образом:

public class classA
{
    private classB b = new classB();

    public void methodA()
    {
        lock (b)
        {
            // Operation without calling methodA() or methodB() 
            // Read b.myVar
            // Update b.myVar
        }
    }

    public void methodB()
    {
        lock (b)
        {
            // Operation without calling methodA() or methodB()
            // Read b.myVar
            // Update b.myVar
        }
    }
}

В другом примере, если b.myVar - это некоторый тип коллекции, который должен быть синхронизирован как кеш, он выглядит следующим образом:

public class classA
{
    private classB b = new classB();

    public void methodA()
    {
        // Read b.myVar for missing collection item

        lock (b)
        {
            // Check for missing collection item again. If not missing, leave lock
            // Operation without calling methodA() or methodB() 
            // Read b.myVar
            // Update b.myVar with new array item
        }
    }

    public void methodB()
    {
        // Read b.myVar for missing collection item
        lock (b)
        {
            // Check for missing collection item again. If not missing, leave lock
            // Operation without calling methodA() or methodB() 
            // Read b.myVar
            // Update b.myVar with new array item
        }
    }
}

Ответ 5

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

Ответ 6

Самое простое решение: не используйте экземпляр ClassB среди ваших потоков.

Другими словами, создайте новый ClassB с объявлением потока и отправьте его как параметр.