Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
452 views
in Technique[技术] by (71.8m points)

c# - ContinueWith loses the SynchronizationContext

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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();
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...