Как SemaphoreSlim.WaitAsync допускает последовательное выполнение асинхронной функции? - PullRequest
1 голос
/ 30 марта 2019

Контекст для этого вопроса - приложение WPF.Приложения WPF используют DispatcherSynchronizationContext.

Если у меня в приложении есть кнопка, которая вызывает метод-обработчик Button_Click, и я хочу убедиться, что весь код в этой функции выполняется только одним потоком, я бы обернул ее в семафор, как показано?Но я не понимаю, как это работает.

Если предположить, что кнопка была нажата, мы нажмем WaitAsync (), которая возвращает задание, которое завершается при вводе семафора, так что я думаю сразу?Затем мы нажимаем await GetLengthAsync (), который возвращает нас обратно в цикл сообщений wpf.Если предположить, что прошло 10 секунд, и кнопка снова нажата, то мы бы снова ввели метод Button_Click и нажали WaitAsync (), которая возвращает задачу, которая завершается, когда мы входим в семафор, и мы не можем войти в семафор, поэтому мы возвращаемся назад кцикл сообщений?вот как это работает?

ОСНОВНОЙ ВОПРОС - Оба раза мы нажимаем WaitAsync (), мы находимся в одном потоке, и наш семафор ограничивает параллелизм, чтобы позволить только одному потоку одновременно выполнять этот блок кода, ноэто не позволит нашему потоку также ввести этот код?Семафор, очевидно, не может быть получен, скажем, некоторыми другими потоками, такими как thread4 или thread5, но он также не может быть получен даже тем же самым потоком снова?Любое разъяснение будет с благодарностью.

private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);

public async void Button_Click(object sender, EventArgs args)
{
    await semaphoreSlim.WaitAsync();

    try
    {
        // GetLengthAsync takes 40 seconds to complete
        int length = await GetLengthAsync();

        // LongComputeFunc takes 30 seconds to complete
        int aggregate = LongComputeFunc(length);
    }
    finally
    {
        semaphoreSlim.Release();
    }
}

1 Ответ

0 голосов
/ 01 апреля 2019

Если предположить, что кнопка была нажата, мы нажмем WaitAsync (), которая возвращает задание, которое завершается при вводе семафора, так что я думаю сразу? Затем мы нажимаем await GetLengthAsync (), который возвращает нас обратно в цикл сообщений wpf.

Да и да.

Если предположить, что прошло 10 секунд и кнопка снова нажата, то мы бы снова ввели метод Button_Click и нажали WaitAsync (), которая возвращает задачу, которая завершается при входе в семафор, и мы не можем войти в семафор, чтобы отскочить вернуться в цикл сообщений? это так работает?

Да.

ОСНОВНОЙ ВОПРОС - Оба раза, когда мы нажимаем WaitAsync (), мы находимся в одном и том же потоке, и наш семафор ограничивает параллелизм, чтобы позволить только одному потоку одновременно выполнять этот блок кода, но не позволяет нашему тому же потоку входить в этот поток код тоже? семафор, очевидно, не может быть получен, скажем, с помощью некоторых других потоков, таких как thread4 или thread5, но он также не может быть получен даже тем же потоком снова?

Это правильно. Некоторые примитивы синхронной координации имеют возможность разрешать рекурсивные блокировки (например, Monitor), а другие - нет (например, Mutex). Однако для примитивов асинхронной координации неестественно поддерживать рекурсивные блокировки. (Лично я против рекурсивной блокировки вообще ). Примитивы синхронной координации могут сойтись с рекурсией, потому что существует понятие «поток», который «владеет» блокировкой. Для примитивов асинхронной координации нет понятия, что thread владеет блокировкой; скорее, «блок кода» владеет блокировкой.

Итак, это многословный способ сказать, что SemaphoreSlim.WaitAsync не является рекурсивным (и не должно быть).

Теперь, хороший ли это дизайн UX или нет - это другой вопрос. Как отмечено в комментариях, для пользовательского интерфейса чаще всего отключают кнопку, которая представляет собой длительную операцию, если вы хотите, чтобы только одна запускалась одновременно. Тем не менее, вы могли бы использовать подход SemaphoreSlim, если вы хотите , чтобы позволить пользователю поставить в очередь более одной длительной операции. В этом случае SemaphoreSlim действует как своего рода неявная очередь для вашего кода.

...