Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My desktop application hangs on web API service call with async and await

I am getting lots of delay when saving data in database. I have one exe (Deskptop Application) which reads data from serial port and push that entry in to database through web API service but my application get hangs on this line:

httpClient.PostAsync("api/MyController/Save", httpConent).Result;

This exe is responsible to call my web API service method and save data to my database.

This is my code:

void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
       {
           int dataLength = _serialPort.BytesToRead;
           byte[] data = new byte[dataLength];
           int nbrDataRead = _serialPort.Read(data, 0, dataLength);

           if (nbrDataRead == 0)
               return;

           // Send data to whom ever interested
           if (NewSerialDataRecieved != null)
           {
               NewSerialDataRecieved(this, new SerialDataEventArgs(data));
           }
       }

 void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
        {
            if (this.InvokeRequired)
            {
                // Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
                //// Fired-off asynchronously; let the current thread continue.
                this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
                return;
            }
            //data is converted to text
            string str = Encoding.ASCII.GetString(e.Data);
            if (!string.IsNullOrEmpty(str))
            {
               CallWebservice(str)
            }
        }

public void CallWebservice(string xmlRequest)
        {
            using (var httpClient = new HttpClient())
            {
               httpClient.BaseAddress = new Uri("WebService Url");
                httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                StringContent httpConent = new StringContent(xmlRequest, Encoding.UTF8);
                HttpResponseMessage responseMessage = null;
                try
                {
                    responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;
                }
                catch (Exception ex)
                {
                    if (responseMessage == null)
                    {
                        responseMessage = new HttpResponseMessage();
                    }
                    responseMessage.StatusCode = HttpStatusCode.InternalServerError;
                    responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
                }
            }
        }

My web api method:

 public async Task<HttpResponseMessage> Save(HttpRequestMessage request)
        {
               var requestdata = request.Content.ReadAsStringAsync().Result;//extract Users Id's from this
                var users=context.User.Where(t => (t.Stats == userId1) || (t.Stats == userId2)).ToList();
                var objUsersMapping= new UsersMapping();
                objUsersMapping.Work1 = users[0].Work1;
                objUsersMapping.Work2 = users[1].Work1;
                await this.SaveUsersMapping(objUsersMapping); 
        }

 public async Task<UsersMapping> SaveUsersMapping(UsersMapping objUsersMapping)
        {
            using (var context = new MyEntities())
            {
                try
                {
                    context.UsersMapping.Add(objUsersMapping);
                    await context.SaveChangesAsync();
                    return objUsersMapping;
                }
                catch (Exception foExe)
                {
                    return null;
                }
            }
        }

I haven't work much on Windows application so I am not understanding why my application is hanging.

Note: data will be continuously coming to my serial port so saving data through web service should not disturb _serialPort_DataReceived event.

like image 541
Learning-Overthinker-Confused Avatar asked May 16 '16 07:05

Learning-Overthinker-Confused


People also ask

What is async hang in ASP NET?

What is an async hang? Many modern ASP.NET applications are developed using the asynchronous task pattern. This pattern helps reduce the thread pool exhaustion issues traditionally associated with synchronous applications. That said, async ASP.NET applications can and do still experience hangs!

How do I run a async request in Visual Studio?

Request a Product. For example, client.GetAsync<Product> ("api/products/4");. In Visual Studio, create a new Windows console app named HttpClientSample and paste in the following code: The preceding code is the complete client app. RunAsync runs and blocks until it completes.

Why is my httpclient method async?

Most HttpClient methods are async, because they perform network I/O. All of the async tasks are done inside RunAsync. Normally an app doesn't block the main thread, but this app doesn't allow any interaction. Use NuGet Package Manager to install the Web API Client Libraries package.

When async method attempts to continue it first enters the captured context?

When the async method attempts to continue, it first re-enters the captured "context" (in this case, an ASP.NET SynchronizationContext ). The ASP.NET SynchronizationContext only permits one thread in the context at a time, and there is already a thread in the context - the thread blocked on Task.Result. Use async all the way down.


1 Answers


This is a summary of my comments beneath the OP's question


You are calling an asynchronous method synchronously. That will cause the current thread to block. Get rid of the .Result and alter the rest of the code accordingly (like including async and await there too).

e.g. change this line

responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;

...to:

 responseMessage = await httpClient.PostAsync("api/MyController/Save", httpConent);

Your method signature will need to be changed as follows:

public async Task CallWebservice(string xmlRequest)
{

}

Any method that calls it will also need to be async and use await for example your _spManager_NewSerialDataRecieved() method.

Note it has been changed from void to async void. Note too the await prior to CallWebservice().

async void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
    {
        if (this.InvokeRequired)
        {
            // Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
            //// Fired-off asynchronously; let the current thread continue.
            this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
            return;
        }
        //data is converted to text
        string str = Encoding.ASCII.GetString(e.Data);
        if (!string.IsNullOrEmpty(str))
        {
           await CallWebservice(str)
        }
    }

A note on async void

Because the above method is an event handler it is fine for the method to be async void. Generally you want to avoid async void in non event handler code. For more info see this brilliant article by Mr Stephen Cleary.

Is this the only problem sir??

You should fix your async Save() method on the server too as it also has a .Result(). That will block the current thread on the server. Prefix it with a await. Generally you want to avoid .Result as a means to wait for the task to complete. It is safe to use as a means to obtain the result after you have awaited it, but there are more elegant ways to await and get the result in a single line of code. e.g. x = await FooAsync();.

like image 113
MickyD Avatar answered Oct 04 '22 03:10

MickyD