Вопрос

In the snippet below, the SynchronizationContext is lost, and because of that also the CurrentCulture and CurrentUICulture. Log() comes from this answer.

public async Task<ActionResult> Index()
{
    Log("before GetAsync");
    await new HttpClient().GetAsync("http://www.example.com/")
        .ContinueWith(request =>
        {
            Log("ContinueWith");
            request.Result.EnsureSuccessStatusCode();
        }, TaskContinuationOptions.AttachedToParent);

    return View();
}

static void Log(string message)
{
    var ctx = System.Threading.SynchronizationContext.Current;
    System.Diagnostics.Debug.Print("{0}; thread: {1}, context: {2}, culture: {3}, uiculture: {4}",
        message,
        System.Threading.Thread.CurrentThread.ManagedThreadId,
        ctx != null ? ctx.GetType().Name : String.Empty,
        System.Threading.Thread.CurrentThread.CurrentCulture.Name,
        System.Threading.Thread.CurrentThread.CurrentUICulture.Name);
}

This is the output:

before GetAsync; thread: 56, context: AspNetSynchronizationContext, culture: nl, uiculture: nl
ContinueWith; thread: 46, context: , culture: nl-BE, uiculture: en-US

Before the GetAsync, the culture and UI culture have the values I set in Application_BeginRequest. Inside the ContinueWith, the context is missing, the culture is set to what's provided by the browser, and UI culture is set to some default.

From what I understand, everything with AspNetSynchronizationContext should be happening automatically. What's wrong with my code?

Это было полезно?

Решение

In order to force scheduling of the continuation on the request context thread, you need to specify the TaskScheduler that should be used when scheduling the continuation.

public async Task<ActionResult> Index()
{
    Log("before GetAsync");
    await new HttpClient().GetAsync("http://www.example.com/")
        .ContinueWith(request =>
        {
            Log("ContinueWith");
            request.Result.EnsureSuccessStatusCode();
        }, 
        TaskContinuationOptions.AttachedToParent,
        CancellationToken.None,
        TaskScheduler.FromCurrentSynchronizationContext());

    return View();
}

Howver, you are using await which automatically marshals continuations on to the current SynchronizationContext. You should be able to do this:

public async Task<ActionResult> Index()
    {
        Log("before GetAsync");
        HttpResponseMessage request = await new HttpClient().GetAsync("http://www.example.com/");

        //everything below here is you 'continuation' on the request context
        Log("ContinueWith");
        request.EnsureSuccessStatusCode();

        return View();
    }

Другие советы

Did you try with TaskContinuationOptions.ExecuteSynchronously? It should ran the continuation task in the same thread...

http://msdn.microsoft.com/en-us/library/vstudio/system.threading.tasks.taskcontinuationoptions

"Specifies that the continuation task should be executed synchronously. With this option specified, the continuation will be run on the same thread that causes the antecedent task to transition into its final state. If the antecedent is already complete when the continuation is created, the continuation will run on the thread creating the continuation. Only very short-running continuations should be executed synchronously."

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top