Путаница по поводу оператора блокировки в C # - PullRequest
20 голосов
/ 08 марта 2012

Это от MSDN: Ключевое слово lock гарантирует, что один поток не введет критическую секцию кода, в то время как другой поток находится в критической секции .

Должна ли критическая секция быть такой же, как критическая секция ?

Или это значит: Ключевое слово lock гарантирует, что один поток не войдет в любую критическую секцию, защищенную объектом кода, в то время как другой поток находится в любой критической секции, защищенной тем же объектом .

    class Program
{
    static void Main(string[] args)
    {
        TestDifferentCriticalSections();

        Console.ReadLine();
    }

    private static void TestDifferentCriticalSections()
    {
        Test lo = new Test();

        Thread t1 = new Thread(() =>
        {
            lo.MethodA();
        });
        t1.Start();

        Thread t2 = new Thread(() =>
        {
            lo.MethodB();
        });
        t2.Start();
    }
}

public class Test
{
    private object obj = new object();

    public Test()
    { }

    public void MethodA()
    {
        lock (obj)
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("A");
            }
        }
    }

    public void MethodB()
    {
        lock (obj)
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("B");
            }
        }
    }
}

Ответы [ 6 ]

64 голосов
/ 08 марта 2012

Вопрос озадачен, и ответы до сих пор не совсем ясны.Позвольте мне перефразировать вопрос на несколько вопросов:

(1) Гарантирует ли оператор блокировки, что в теле оператора блокировки одновременно находится не более одного потока?

Нет .Например:

static readonly object lock1 = new object();
static readonly object lock2 = new object();
static int counter = 0;
static object M()
{
    int c = Interlocked.Increment(ref counter);
    return c % 2 == 0 ? lock1 : lock2;
}

...
lock(M()) { Critical(); }

Возможно, чтобы два потока одновременно находились в теле оператора блокировки, поскольку оператор блокировки блокирует два разных объекта.Поток Alpha может вызвать M () и получить lock1, а затем поток Beta может вызвать M () и получить lock2.

(2) Предполагая, что мой оператор блокировки всегда блокируется на одном и том же объекте, гарантирует ли оператор блокировки, что в теле тела одновременно находится не более одного «активного» потока?

Да.Если у вас есть:

static readonly object lock1 = new object();
...
lock(lock1) { Critical(); }

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

(3) Предполагая, что у меня есть два оператора блокировки, и оба оператора блокировки каждый раз блокируют один и тот же объект, оператор блокировки гарантирует, что не более одного «активного» потока находится в теле любой блокировки одновременно?

Да.Если у вас есть:

static readonly object lock1 = new object();
...
static void X() 
{
    lock(lock1) { CriticalX(); }
}
static void Y() 
{
    lock(lock1) { CriticalY(); }
}

, то, если нить Альфа находится в X и берет блокировку, а бета нити находится в Y, тогда бета нити будет блокировать до тех пор, пока блокировка не будет доступна до вводатело замка.

(4) Почему вы помещаете «активный» в «кавычки»?

Чтобы обратить внимание на тот факт, что это возможно для ожидание нить будет в корпусе замка.Вы можете использовать метод Monitor.Wait, чтобы «приостановить» поток, находящийся в теле блокировки, и позволить заблокированному потоку стать активным и войти в это тело блокировки (или другое тело блокировки, которое блокирует тот же объект).Ожидающая нить будет оставаться в состоянии «ожидания» до тех пор, пока не произойдет импульс.Через некоторое время после импульса он снова присоединяется к очереди «готовности» и блокирует , пока в блокировке не останется «активный» поток.Затем он возобновляется в том месте, где остановился.

5 голосов
/ 08 марта 2012

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

Пример:

public static object DatabaseLck= new object();

lock (DatabaseLck) {
        results = db.Query<T>(query).ToList();
     }

Или

lock (DatabaseLck) {
       results = db.Query<T>(string.Format(query, args)).ToList();
  }

Ни один из этих блоков кода не может быть запущен одновременно, ПОТОМУ ЧТО они используют один и тот же объект блокировки.Если вы используете разные объекты блокировки для каждого из них, они могут работать одновременно.

3 голосов
/ 08 марта 2012

Это один и тот же критический раздел.

lock (synclock)
{
  // the critical section protected by the lock statement
  // Only one thread can access this at any one time
}

См. оператор блокировки на MSDN:

Ключевое слово lock помечает блок оператора как критический раздел, получая блокировку взаимоисключения длязаданный объект, выполнив оператор, а затем сняв блокировку.


Или это означает: ключевое слово lock гарантирует, что один поток не войдет в какой-либо критический раздел кода, а другойТема есть в любом критическом разделе.?

Нет .Это не значит что.Это означает критическую секцию, защищенную этой блокировкой и только этой блокировкой.


Обновление, следующий пример кода:

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

1 голос
/ 08 марта 2012

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

0 голосов
/ 08 марта 2012

Критическим разделом, о котором идет речь, является раздел, защищенный операторами блокировки.

Любой критический участок, который блокируется на том же объекте, будет заблокирован для получения доступа.

Также важно, чтобы ваш объект блокировки был статическим, потому что блокировки должны блокировать (или пытаться заблокировать) один и тот же экземпляр объекта блокировки.

0 голосов
/ 08 марта 2012

Нет, это означает, что другой поток не войдет в критическую секцию, защищенную этим оператором блокировки.

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

Итак, перевод: ключевое слово lock обеспечиваетпоток не входит в раздел кода, защищенный lock, в то время как другой поток находится в этом разделе кода (защищен lock)

...