Ожидают ли потоки блокировки FIFO? - PullRequest
6 голосов
/ 26 августа 2009

Допустим, у меня есть следующий код

static class ...
{
    static object myobj = new object();

    static void mymethod()
    {
        lock(myobj)
        {
            // my code....
        }
    }
}

Тогда, скажем, пока у thread1 есть блокировка, thread2 пытается запустить mymethod. Будет ли он ждать, пока блокировка не будет снята или возникнет исключение?

Если он действительно ждет, обеспечен ли порядок, чтобы при поступлении дополнительных потоков они были FIFO?

Ответы [ 5 ]

8 голосов
/ 26 августа 2009

Обновлено мой ответ: Они поставлены в очередь, но порядок не гарантирован. FIFO.

Проверьте эту ссылку: http://www.albahari.com/threading/part2.aspx

3 голосов
/ 26 августа 2009

Из вашего кода не ясно, как myobj становится видимым внутри mymethod. Похоже, var myobj является локальной переменной стека в области объявления (так как var). В этом случае может случиться так, что у каждого потока будет отдельный экземпляр, и mymethod не будет блокироваться.

Обновление

Для всего аргумента FIFO необходима некоторая справочная информация: CLR не обеспечивает синхронизацию. Именно CLR host обеспечивает это как service для среды CLR. Хост реализует IHostSyncManager и другие интерфейсы и предоставляет различные примитивы синхронизации. Это может показаться неуместным, так как наиболее распространенный хост - это типичный хост приложения (т. Е. Вы компилируете в и исполняете), и это препятствует любой синхронизации с ОС (ваши старые примитивы книг Petzold в Win32 API). Однако есть еще как минимум две основные среды размещения: ASP.Net (я не уверен, что это делает) и SQL Server. Что я могу сказать наверняка, так это то, что SQL Server предоставляет все примитивы на вершине SOS (в основном это операционная система пользователя), никогда не касаясь примитивов ОС, а примитивы SOS несправедливы Избегайте конвоев с замками (т.е. без гарантии без FIFO). Как уже указывалось в ссылке в другом ответе, примитивы ОС начали также обеспечивать недобросовестное поведение по той же причине, что и избегание конвоев блокировки.

Для получения дополнительной информации о конвойных замках вы должны прочитать статьи Рика Вики в Проектирование приложений для высокой производительности :

Замковый конвой

замки FIFO гарантируют справедливость и вперед прогресс за счет вызывая блокировку конвоев. Семестр изначально имел ввиду несколько потоков выполнение той же части кода, что и группа, приводящая к более высоким столкновениям чем если бы они были случайным образом распределены по всему коду (очень похоже на автомобили, сгруппированные в пакеты на светофоре). Конкретный Феномен, о котором я говорю, хуже потому что однажды он образует неявное передача владения замком сохраняет темы в замке шаг.

Для иллюстрации рассмотрим пример где поток содержит блокировку и получает приоритет, удерживая замок. В результате все остальные темы будет накапливаться в списке ожидания для этого замок. Когда вытесненная нить (блокировка владелец в это время) снова запускается и снимает блокировку, это автоматически передает право собственности на заблокировать первый поток в ожидании список. Этот поток не может работать для некоторое время, но часы «время удержания» тикает. Предыдущий владелец обычно запрашивает блокировку снова, прежде чем список ожидания очищен, увековечивая конвой

1 голос
/ 12 марта 2016

Простой пример говорит нам, что заказ не гарантированно будет FIFO

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;


namespace ConsoleApplication
{
    class Program
    {
        private static Info info = new Info();

        static void Main(string[] args)
        {
            Thread[] t1 = new Thread[5];
            for (int i = 0; i < 5; i++)
            {
                t1[i] = new Thread(info.DoWork);
            }

            Thread[] t2 = new Thread[5];
            for (int i = 0; i < 5; i++)
            {
                t2[i] = new Thread(info.Process);
            }

            for (int i = 0; i < 5; i++)
            {
                t1[i].Start();
                t2[i].Start();
            }

            Console.ReadKey();
        }
    }

    class Info
    {
        public object SynObject = new object();

        public void DoWork()
        {
            Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
            lock (this.SynObject)
            {
                Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }

        public void Process()
        {
            Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
            lock (this.SynObject)
            {
                Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;


namespace ConsoleApplication
{
    class Program
    {
        private static Info info = new Info();

        static void Main(string[] args)
        {
            Thread[] t1 = new Thread[5];
            for (int i = 0; i < 5; i++)
            {
                t1[i] = new Thread(info.DoWork);
            }

            Thread[] t2 = new Thread[5];
            for (int i = 0; i < 5; i++)
            {
                t2[i] = new Thread(info.Process);
            }

            for (int i = 0; i < 5; i++)
            {
                t1[i].Start();
                t2[i].Start();
            }

            Console.ReadKey();
        }
    }

    class Info
    {
        public object SynObject = new object();

        public void DoWork()
        {
            Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
            lock (this.SynObject)
            {
                Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }

        public void Process()
        {
            Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId);
            lock (this.SynObject)
            {
                Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
}

Выполнение будет происходить примерно так

Process Lock Reached: 15
Process Lock Enter: 15
DoWork Lock Reached: 12
Process Lock Reached: 17
DoWork Lock Reached: 11
DoWork Lock Reached: 10
DoWork Lock Reached: 13
DoWork Lock Reached: 9
Process Lock Reached: 18
Process Lock Reached: 14
Process Lock Reached: 16
Process Lock Exit: 15
Thread Lock Enter: 9
Thread Lock Exit: 9
Process Lock Enter: 14
Process Lock Exit: 14
Thread Lock Enter: 10
Thread Lock Exit: 10
Thread Lock Enter: 11
Thread Lock Exit: 11
Process Lock Enter: 16
Process Lock Exit: 16
Thread Lock Enter: 12
Thread Lock Exit: 12
Process Lock Enter: 17
Process Lock Exit: 17
Thread Lock Enter: 13
Thread Lock Exit: 13
Process Lock Enter: 18
Process Lock Exit: 18

Как вы видите, процесс блокировки досягаемости отличается от блокировки ввода.

0 голосов
/ 26 августа 2009

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

Для всех практических целей вы можете предполагать, что заказ будет FIFO; однако следует помнить об этой проблеме.

0 голосов
/ 26 августа 2009

Это будет ждать, и они НЕ будут в том же порядке.

В зависимости от ваших потребностей у вас может быть больше производительности, если вы посмотрите на что-то вроде ReaderWriterLock или что-то отличное от lock

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