Понимание проблем SemaphoreSlim? - PullRequest
       35

Понимание проблем SemaphoreSlim?

1 голос
/ 20 октября 2019

У меня есть простой тестовый код здесь с SemaphoreSlim

        static SemaphoreSlim mSemaphore = new SemaphoreSlim(3);

        static async Task Main()
        {
            var tasks = new Task[5];

            for (var i = 0; i < tasks.Length; i++)
            {
                var taskNo = i;
                tasks[i] = Task.Run(() => DoWork($"task{taskNo}"));
            }

            await Task.WhenAll(tasks);
        }

        static async Task DoWork(string taskName)
        {
            for (var i = 0; i < 3; i++)
            {
                mSemaphore.Wait();

                Console.WriteLine($"{taskName}: doing {i}.");   
                await Task.Delay(1000); 

                mSemaphore.Release();
            }
        }

, если я прав: в моем контексте мой Семафор должен позволять выполнять только 3 Задачи, а затем он должен их выпустить, а послечто это должно позволить моим двум другим Задачам выполнить свою работу.

Проблема

Я проверил это, но, к сожалению, иногда я получаю разные / неправильные результаты.

вот 2 результата на выходе.

ВЫХОД

enter image description here

Ответы [ 2 ]

1 голос
/ 20 октября 2019

Вызовы mSemaphore.Wait() и mSemaphore.Release() внутри вашего for цикла. После каждой итерации цикла каждая задача освобождает семафор, а затем пытается получить его снова.

Учитывая это, ничто не мешает задаче 0, 1 или 2 освободить семафор в конце циклаи задача3 от его приобретения. Выпустившая его задача вернется к началу цикла и снова сядет на mSemaphore.Wait(), ожидая, пока другая задача освободит семафор.

Все ваши задачи выполняются одновременно (кромеиз очень маленькой начальной задержки, потенциально), и все они имеют равный приоритет (SemaphoreSlim не гарантирует порядок, в котором вещи, ожидающие семафор, приобретают его - это, по сути, случайное, какая задача ожидания будет ей предоставлена). Поэтому неудивительно, что иногда задачи 0, 1 и 2 завершаются до того, как задача 3 получает семафор, а иногда все происходит в другом порядке.

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

0 голосов
/ 20 октября 2019

Метод SemaphoreSlim.Wait:

Блокирует текущий поток, пока он не сможет ввести SemaphoreSlim.

Метод SemaphoreSlim.WaitAsync:

Асинхронно ожидает ввода SemaphoreSlim.

Поскольку ваш код асинхронный, вы должны использовать WaitAsync, а не Wait,Блокируя потоки во время выполнения асинхронного кода, вы получите все виды забавных эффектов вместо ожидаемого поведения. Рассмотрим, например, что после await Task.Delay(1000) ваш асинхронный рабочий процесс может продолжаться в другом потоке или не работать в зависимости от условий, которые вы не контролируете.

Короче говоря, просто замените mSemaphore.Wait() на await mSemaphore.WaitAsync() и все будет хорошо.

...