Невозможно использовать «ждать» более одного раза - PullRequest
0 голосов
/ 12 апреля 2020

Я попал в эту очень странную ситуацию, когда я могу использовать await только один раз. См. Пример ниже:

private async Task RegisterInstruments(byte clientId, RegisterInstrumentRequest registerInstrumentRequest, bool isPriority = false)
{
  if (registerInstrumentRequest != null && registerInstrumentRequest.RegisterIntrstuments != null)
  {
    var r1 = await instrumentManager.GetInstrumentAsync("AMZN");
    logger.Debug("reached r1");
    var r2 = await instrumentManager.GetInstrumentAsync("AMZN");
    logger.Debug("reached r2");
  }
}

Выход Serilog:

reached r1

Когда я запускаю отладчик и выполняю построчно, я могу выполнить до строки var r1 = ... и дать ожидаемый результат в r1, затем я нажимаю F10 , и отладчик просто завершает sh, как я бы нажал «Продолжить», и появится мой пользовательский интерфейс winform.

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

Кроме того, он не печатает ничего необычного в окне «Вывод» в разделе «Отладка».

Что я пробовал:

  1. Используйте try/catch, но он также никогда не попадет внутрь блока catch.
  2. Вызов GetInstrumentAsync с использованием .Result и без await. Это просто то же самое.
  3. Я разработал нормальную версию этого метода (без async/await) и использую его, как ожидается.
  4. Попробуйте запустить без отладчика (Ctrl + F5), чтобы убедиться, что это не так. Проблема, связанная с отладчиком. Но поведение остается тем же.

Я не могу понять, почему это происходит.

Код для GetInstrumentAsync метода:

public async Task<DsInstrument> GetInstrumentAsync(string fullName)
{
  return await _dbContext.Instruments
    .AsNoTracking()
    .SingleOrDefaultAsync(m => m.FullName == fullName);
}

Остальная часть код (некоторые выражения удалены для краткости):

public SocketManager(Framework framework)
{
  socket.ReceiveReady += SocketServer_ReceiveReady;
}

// I cannot make this `async Task` as this is an even listener. It must return void not Task. So I am using .Wait()
private void SocketServer_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
  ProcessMessage(e.Socket.ReceiveMultipartMessage()).Wait();
}

private async Task ProcessMessage(NetMQMessage netMQFrames, bool isPriority = false)
{
  RequestMessageType messageType = (RequestMessageType)netMQFrames[2].ConvertToInt32();

  byte clientId = netMQFrames[0].Buffer[0];
  byte[] messagBuffer = netMQFrames.FrameCount > 3 ? netMQFrames[3].Buffer : null;

  switch (messageType)
  {
    // Remved other cases for brevity

    case RequestMessageType.RegisterInstruments:
      RegisterInstrumentRequest registerInstrumentRequest = MessageParser.Deserialize<RegisterInstrumentRequest>(messagBuffer);
      await RegisterInstruments(clientId, registerInstrumentRequest, isPriority);
      break;
  }
}

1 Ответ

1 голос
/ 12 апреля 2020

Метод GetInstrumentAsync вызывается фоновым потоком и использует то, что похоже на повторно используемый экземпляр класса DbContext (идентификатор _dbContext). Этот класс Entity Framework не является потокобезопасным , поэтому будьте осторожны при использовании его в многопоточном приложении. Либо синхронизируйте доступ к одному экземпляру, используя блокировки, либо просто создайте новый экземпляр при каждом запросе базы данных.

...