Семафор в обработчике событий KeyUp - блокирует ввод с клавиатуры - PullRequest
0 голосов
/ 19 июня 2019

Я пытаюсь понять семафор .Короче говоря, я поместил «длительную» процедуру (которая обращается к сетевым ресурсам), InitializeNamesAsync("","",""), в KeyUp event handler.Я пытаюсь позволить пользователю выполнять непрерывную печать без замедления, пока viewNames инициализируется InitializeNamesAsync().Поскольку пользователь будет непрерывно печатать, KeyUp event handler будет вызываться много раз, пока работает метод InitializeNamesAsync().

Хотя приведенный ниже код компилируется нормально, он навсегда блокирует ввод с клавиатуры.

Итак, мои вопросы:

  1. это правильное использование семафора?
  2. как я могу сделать эту работу?
  3. Есть лилучше?

TIA

Определившись

ResourceLock = new Semaphore(0, 1);


private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
    {
        if (viewNames == null)
        {

            ResourceLock.WaitOne();
            await InitializeNamesAsync("", "", "");
            ResourceLock.Release();

        }
   }

Ответы [ 2 ]

2 голосов
/ 19 июня 2019

Есть фундаментальная проблема с вашим дизайном, хотя вы используете Semaphore, чтобы позволить нескольким потокам входить и выполнять событие внутри критической области, но проблема в том, , какой поток вы блокируете?

Поскольку событие выполняется на Ui thread, который является просто 1 и уникальным, происходит следующее:

  • Ваш код входит в событие, вызывает WaitOne для семафорав потоке пользовательского интерфейса, и все готово, вы заблокированы, он даже не выполняет метод Async, как ожидалось

Проверьте следующий код консоли, как вы думаете, что в результате?

Следующий код ведет к тупику, поскольку пользовательский интерфейс или основная консольная нить ожидают себя

async Task Main()
{

    Semaphore s = new Semaphore(0, 2);
    for(int x = 0; x < 5;x++)
    {
        s.WaitOne();
        await Test(x);      
        s.Release();
    }    
}

async Task Test(int x)
{
    $"Entering : {x}".Dump();
    await Task.Delay(3000);
}
  • В указанном выше коде await Test(x); иs.Release(); никогда не называются

Какие есть варианты, просмотрите измененный дизайн:

async Task Main()
{   
    for(int x = 0; x < 5;x++)
    {
        await Test(x);
        s.WaitOne();
    }   
}

Semaphore s = new Semaphore(0,2);

async Task Test(int x)
{
    $"Entering : {x}".Dump();
    await Task.Delay(3000);
    s.Release();
}

Что здесь отличается:

  1. Асинхронный метод был вызван до Semaphore WaitOne называется
  2. Семафор Release происходит после завершения метода Async, а не в том же потоке (в данном случае в потоке пулов потоков)

И вы найдете этокод будет успешно выполнен без какой-либо тупиковой ситуации

Каково решение:

  1. Не вызывайте WaitOne в уникальном потоке, таком как поток пользовательского интерфейса, это рецептдля взаимоблокировки, особенно когда Release также запланировано в том же потоке
  2. Вызов Release в отдельном потоке (я использовал метод Async, который в данном случае использует поток Threadpool)

Другие детали:

  • В идеале семафор предназначен для нескольких потоков, чтобы войти в критическую область, если вы ожидаете только одну нить, то Семпахора может не бытьправильный выбор, но это помогает сигнализировать потоки в отличие от блокировки, вы также можете просмотреть ManualResetEvent и AutoResetEvent, который поддерживает Signaling / EventWaitHandle сценарии использования
1 голос
/ 19 июня 2019

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

Как я понимаю, вам нужно запустить инициализацию в фоновом режиме.

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

beingInitialized

и инициализируется с кодом типа

private async void EnsureInitialized()
{

    if(!initialized && !beingInitialized)
   {
    beingInitalized = true;
    await StartInitialization();
    initalized = true;
    beingInitialized = false;
   }
}

И тогда назови это огнем и забудь

как

private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
    EnsureInitialized();
    ...
...