D'accordo con la risposta di @Darrel Miller. Questo è un bug Aggiungendo solo più dettagli per il bug report.
Il problema è che internamente un TaskCompletionSource
viene utilizzato, ma quando viene generata un'eccezione causa della cancellazione in questo caso specifico, esso non si trova, e la TaskCompletionSource
non è impostato in uno degli stati completati (e quindi, . in attesa di 's Task
il TaskCompletionSource
non tornerà mai
Utilizzando ILSpy, guardando HttpClientHandler.SendAsync
potete vedere la TaskCompletionSource
:
// System.Net.Http.HttpClientHandler
/// <summary>Creates an instance of <see cref="T:System.Net.Http.HttpResponseMessage" /> based on the information provided in the <see cref="T:System.Net.Http.HttpRequestMessage" /> as an operation that will not block.</summary>
/// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.</returns>
/// <param name="request">The HTTP request message.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="request" /> was null.</exception>
protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request", SR.net_http_handler_norequest);
}
this.CheckDisposed();
if (Logging.On)
{
Logging.Enter(Logging.Http, this, "SendAsync", request);
}
this.SetOperationStarted();
TaskCompletionSource<HttpResponseMessage> taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
HttpClientHandler.RequestState requestState = new HttpClientHandler.RequestState();
requestState.tcs = taskCompletionSource;
requestState.cancellationToken = cancellationToken;
requestState.requestMessage = request;
this.lastUsedRequestUri = request.RequestUri;
try
{
HttpWebRequest httpWebRequest = this.CreateAndPrepareWebRequest(request);
requestState.webRequest = httpWebRequest;
cancellationToken.Register(HttpClientHandler.onCancel, httpWebRequest);
if (ExecutionContext.IsFlowSuppressed())
{
IWebProxy webProxy = null;
if (this.useProxy)
{
webProxy = (this.proxy ?? WebRequest.DefaultWebProxy);
}
if (this.UseDefaultCredentials || this.Credentials != null || (webProxy != null && webProxy.Credentials != null))
{
this.SafeCaptureIdenity(requestState);
}
}
Task.Factory.StartNew(this.startRequest, requestState);
}
catch (Exception e)
{
this.HandleAsyncException(requestState, e);
}
if (Logging.On)
{
Logging.Exit(Logging.Http, this, "SendAsync", taskCompletionSource.Task);
}
return taskCompletionSource.Task;
}
in seguito, attraverso la linea Task.Factory.StartNew(this.startRequest, requestState);
otteniamo la seguente Metodo:
// System.Net.Http.HttpClientHandler
private void PrepareAndStartContentUpload(HttpClientHandler.RequestState state)
{
HttpContent requestContent = state.requestMessage.Content;
try
{
if (state.requestMessage.Headers.TransferEncodingChunked == true)
{
state.webRequest.SendChunked = true;
this.StartGettingRequestStream(state);
}
else
{
long? contentLength = requestContent.Headers.ContentLength;
if (contentLength.HasValue)
{
state.webRequest.ContentLength = contentLength.Value;
this.StartGettingRequestStream(state);
}
else
{
if (this.maxRequestContentBufferSize == 0L)
{
throw new HttpRequestException(SR.net_http_handler_nocontentlength);
}
requestContent.LoadIntoBufferAsync(this.maxRequestContentBufferSize).ContinueWithStandard(delegate(Task task)
{
if (task.IsFaulted)
{
this.HandleAsyncException(state, task.Exception.GetBaseException());
return;
}
contentLength = requestContent.Headers.ContentLength;
state.webRequest.ContentLength = contentLength.Value;
this.StartGettingRequestStream(state);
});
}
}
}
catch (Exception e)
{
this.HandleAsyncException(state, e);
}
}
Si noterà che il delegato nella chiamata a ContinueWithStandard
non ha la gestione delle eccezioni all'interno del delegato, e nessuno tiene al compito tornato (e quindi quando questa operazione genera un'eccezione, è ignorato). La chiamata a this.StartGettingRequestStream(state);
fa un'eccezione:
System.Net.WebException occurred
HResult=-2146233079
Message=The request was aborted: The request was canceled.
Source=System
StackTrace:
at System.Net.HttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
InnerException:
Ecco il completo stack di chiamate al momento dell'eccezione:
> System.dll!System.Net.HttpWebRequest.BeginGetRequestStream(System.AsyncCallback callback, object state) Line 1370 C#
System.Net.Http.dll!System.Net.Http.HttpClientHandler.StartGettingRequestStream(System.Net.Http.HttpClientHandler.RequestState state) + 0x82 bytes
System.Net.Http.dll!System.Net.Http.HttpClientHandler.PrepareAndStartContentUpload.AnonymousMethod__0(System.Threading.Tasks.Task task) + 0x92 bytes
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() Line 59 + 0xc bytes C#
mscorlib.dll!System.Threading.Tasks.Task.Execute() Line 2459 + 0xb bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Line 2815 + 0x9 bytes C#
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581 + 0xd bytes C#
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 530 + 0xd bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Line 2785 C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Line 2728 C#
mscorlib.dll!System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(System.Threading.Tasks.Task task, bool taskWasPreviouslyQueued) Line 91 + 0xb bytes C#
mscorlib.dll!System.Threading.Tasks.TaskScheduler.TryRunInline(System.Threading.Tasks.Task task, bool taskWasPreviouslyQueued) Line 221 + 0x12 bytes C#
mscorlib.dll!System.Threading.Tasks.TaskContinuation.InlineIfPossibleOrElseQueue(System.Threading.Tasks.Task task, bool needsProtection) Line 259 + 0xe bytes C#
mscorlib.dll!System.Threading.Tasks.StandardTaskContinuation.Run(System.Threading.Tasks.Task completedTask, bool bCanInlineContinuationTask) Line 334 + 0xc bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ContinueWithCore(System.Threading.Tasks.Task continuationTask, System.Threading.Tasks.TaskScheduler scheduler, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskContinuationOptions options) Line 4626 + 0x12 bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ContinueWith(System.Action<System.Threading.Tasks.Task> continuationAction, System.Threading.Tasks.TaskScheduler scheduler, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskContinuationOptions continuationOptions, ref System.Threading.StackCrawlMark stackMark) Line 3840 C#
mscorlib.dll!System.Threading.Tasks.Task.ContinueWith(System.Action<System.Threading.Tasks.Task> continuationAction, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskContinuationOptions continuationOptions, System.Threading.Tasks.TaskScheduler scheduler) Line 3805 + 0x1b bytes C#
System.Net.Http.dll!System.Net.Http.HttpUtilities.ContinueWithStandard(System.Threading.Tasks.Task task, System.Action<System.Threading.Tasks.Task> continuation) + 0x2c bytes
System.Net.Http.dll!System.Net.Http.HttpClientHandler.PrepareAndStartContentUpload(System.Net.Http.HttpClientHandler.RequestState state) + 0x16b bytes
System.Net.Http.dll!System.Net.Http.HttpClientHandler.StartRequest(object obj) + 0x5a bytes
mscorlib.dll!System.Threading.Tasks.Task.InnerInvoke() Line 2835 + 0xd bytes C#
mscorlib.dll!System.Threading.Tasks.Task.Execute() Line 2459 + 0xb bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Line 2815 + 0x9 bytes C#
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581 + 0xd bytes C#
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 530 + 0xd bytes C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Line 2785 C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Line 2728 C#
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Line 2664 + 0x7 bytes C#
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Line 829 C#
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Line 1170 + 0x5 bytes C#
[Native to Managed Transition]
Credo che l'intenzione è quella di non ignorarlo, e nel caso di un l'eccezione chiama il metodo HandleAsyncException
che imposta lo TaskCompletionSource
in uno stato finale.
Potrebbe essersi verificato un deadlock per il blocco del thread principale, si blocca se si prova su un'applicazione di console o se si esegue 'attende l'attività 'invece di' task.Wait'? –
@ScottChamberlain La stessa cosa accade con entrambi i task 'await;' e 'task.Wait'. –
@nick_w, qual è l'ambiente di esecuzione per questo (un'applicazione per l'interfaccia utente desktop, ASP.NET, console, ecc.)? Inoltre, usi 'token.Register 'ovunque? – Noseratio