-2

I am caught into this very weird situation when I can only use await one time. See the example below:

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 output:

reached r1

When I start the debugger and execute line by line, I can execute till line var r1 = ... and gives the expected result into r1 then I press F10 and the debugger will just finish as I would have clicked Continue and my winform UI will appear.

Debugger never returns back to the code when the call is complete. GetInstrumentAsync method is just fetching record from the database using EntityFramework (code below). And that really doesn't take much time.

Also, it doesn't print anything unusual in Output window under Debug section.

What I have tried:

  1. Use try/catch but it never gets inside the catch block too.
  2. Calling GetInstrumentAsync using .Result and without await. It is just same.
  3. I developed the normal version of this method (without async/await) and use it works as expected.
  4. Try to run without debugger (Ctrl + F5) to make sure if it isn't a Debugger related issue. But the behaviour remains the same.

I cannot understand why this is happening.

The code for GetInstrumentAsync method:

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

Rest of the code (some statements removed for brevity) :

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;
  }
}
shashwat
  • 7,851
  • 9
  • 57
  • 90
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/211520/discussion-on-question-by-shashwat-cannot-use-await-more-than-one-time). – Samuel Liew Apr 13 '20 at 02:46

1 Answers1

1

The GetInstrumentAsync method is called by a background thread, and it uses what seems like a reusable instance of a DbContext class (_dbContext identifier). This Entity Framework class is not thread safe, so be careful when using it in a multithreaded application. Either synchronize the access to a single instance using locks, or just create a new instance on every database request.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • Strangely, just for this particular case (NetMQ), I do not get thread safety exception which I did get at some other places in my application when `_dbContext` was being accessed from more than one thread. I did create a separate instance of `_dbContext` in such cases. Not sure why EF never complained about this thread safety issue just for this case. – shashwat Apr 12 '20 at 20:08
  • Probably in this case the _dbContext is called by multiple threads, but not concurrently. The requests may be serialized because of some coincidental reason. If you dislike the idea of creating a new `DbContext` instance on every request, you could store a separate instance per thread by using the [`ThreadLocal`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadlocal-1) class. I am not sure if this is a good idea though. – Theodor Zoulias Apr 12 '20 at 20:17