简体   繁体   中英

ThreadAbortException when calling Task.Result

I have the following code where I'm trying to make a request to a remote endpoint using HttpClient :

using (var client = new HttpClient())
{
   client.BaseAddress = _serviceBaseAddress;

   Task<HttpResponseMessage> readResponseTask = client.GetAsync(relativeUri);
   readResponseTask.Wait();

   using (var response = readResponseTask.Result)
   {
     if (response.StatusCode == HttpStatusCode.NotFound || !response.IsSuccessStatusCode)
     {
       return default(TResult);
     }

     Task<TResult> readContentTask = response.Content.ReadAsAsync<TResult>();
     readContentTask.Wait();

     TResult value = readContentTask.Result;

     return value;
   }
 }

..and occassionally I would get ThreadAbortException at the readResponseTask.Result like so:

System.Threading.ThreadAbortException: Thread was being aborted. at System.Threading.Monitor.ObjWait(Boolean exitContext, Int32 millisecondsTimeout, Object obj) at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)

Under what circumstance will the .Result throw such an exception? I have tried simulating a timeout on the remote endpoint but I got the exception at the .Wait() instead of .Result . Since the exception happens after .Wait() , I'm assuming the result has already been returned from the remote site but somehow something went wrong when it tries to access the result.

Any clues? Could it be something to do with thread concurrency?

I would get ThreadAbortException at the readResponseTask.Result

No, you don't. The call stack clearly shows that it is in fact the Wait() call that produced the exception. Note the frequent appearance of the word "wait" in the trace.

Hard to see how you got confused. Keep in mind that the Task.Result property getter is very small and is going to be inlined when you run the Release build of your program. So you never can see it back in the stack trace.

Perhaps you'll be ahead by simply removing the Wait() call. It isn't necessary, the Result property getter already performs a wait if necessary.

During Wait(), the thread is aborted from outside. Nobody can tell for sure why.

Enabling network client trace can help detecting the root cause.

First, do not use .*Async() if you're going to immediately call .Wait() . This is poor practice and will more than likely lead to errant results. Instead use the synchronous versions of the calls, client.Get(relativeUri) and the following:

TResult value = response.Content.ReadAs<TResult>();    
return value;

If you intend on not taking advantage of the .NET frameworks asynchronous programming model.

But if you would rather take advantage of the asynchronous I/O capabilities, you should do so following the best practices. Use the async/await keywords and make your methods look as though they are synchronous while leveraging the capabilities of the .NET framework's asynchronous keywords.

I can only imagine the entry point to your method looks something like this:

public TResult InvokeClientGet<TResult>(string relativeUri) 
{  
    // ... left out for brevity
}

This is actually preventing you from using the async/await keywords. Instead try the following:

    public async Task<TResult> InvokeClientGet<TResult>(string relativeUri)
    {
        try
        {
            using (var client = new HttpClient { BaseAddress = _serviceBaseAddress })
            {
                using (var response = await client.GetAsync(relativeUri))
                {
                    if (response.StatusCode == HttpStatusCode.NotFound || 
                        !response.IsSuccessStatusCode)
                    {
                        return default(TResult);
                    }

                    return await response.Content.ReadAsAsync<TResult>();
            }
        }
        catch (Exception ex)
        {
            // Handle exceptional conditions
        }
    }

A few words specifically about System.Threading.ThreadAbortException .

When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.

With that being said, if something is calling .Abort() and causing this exception. There isn't much you can do to prevent it. Regardless, try following best practices.

I had the same problem while testing a method via Unit-Tests which was sending Data to a WebService. (ik there are better ways to do that)

The problem was that the Unit-Test called my async method but didn't waited unitl the WebService call had finished. This aborted the WebService call and i got an ThreadAbortException .

[TestMethod]
public void WebServiceTest01(){
    BusinessLogic bs = new BusinessLogic();
    bs.CallWebService();
}

All I needed to do was to make the Unit-Test wait until the WebService call has finished by adding the keyword await .

[TestMethod]
public void WebServiceTest01(){
    BusinessLogic bs = new BusinessLogic();
    var result = await bs.CallWebService();
    Assert.NotNull(result);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM