具有异步/等待的CurrentCulture,自定义同步上下文

Seb*_*hle 11 c# multithreading task-parallel-library

我有一个Web应用程序,我使用async/await来使用大量的异步操作.一切都运行良好,但是当我创建自定义任务以并行运行多个事物时,我注意到,在此任务中,当前文化在等待之后发生了变化.问题似乎是,线程池使用操作系统的文化,这与请求的文化不同,并且默认同步不会更新文化,即使在更改任务中当前线程的文化时也是如此.

所以我创建一个自定义同步上下文:

public sealed class CulturePreservingSynchronizationContext : SynchronizationContext
{
    private CultureInfo culture;
    private CultureInfo cultureUI;

    public CulturePreservingSynchronizationContext()
    {
        GetCulture();
    }

    public void MakeCurrent()
    {
        SetCulture();

        SynchronizationContext.SetSynchronizationContext(CreateCopy());
    }

    public override SynchronizationContext CreateCopy()
    {
        CulturePreservingSynchronizationContext clonedContext = new CulturePreservingSynchronizationContext();
        clonedContext.culture = culture;
        clonedContext.cultureUI = cultureUI;

        return clonedContext;
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        base.Post(s =>
        {
            SetCulture();
            d(s);
        }, state);
    }

    public override void OperationStarted()
    {
        GetCulture();

        base.OperationStarted();
    }

    private void SetCulture()
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUI;
    }

    private void GetCulture()
    {
        culture = CultureInfo.CurrentCulture;
        cultureUI = CultureInfo.CurrentUICulture;
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它.在我的简单示例中它工作正常,但我没有真正理解评估我的方法的细节(顺便说一句:我的os-culture是de-DE).请注意,这只是一个示例,与实际代码无关.我刚刚写了这篇文章来证明等待之后的文化与等待之前的文化不同.

    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

    CulturePreservingSyncContext context = new CulturePreservingSyncContext();

    Task.Run(async () =>
    {
        context.MakeCurrent();

        Console.WriteLine(CultureInfo.CurrentCulture);

        WebClient client = new WebClient();
        string s = await client.DownloadStringTaskAsync(new Uri("http://www.google.de"));

        Console.WriteLine(CultureInfo.CurrentCulture);

    }).Wait();
Run Code Online (Sandbox Code Playgroud)

我们非常欢迎任何建议,了解同步上下文的实现是否良好,如果没有,是否有更好的解决方案.如果异步和等待或任务在我的情况下好或不好,我不想打开讨论.

Ste*_*ary 8

当我创建自定义任务以并行运行多个事物时

区分并发(同时执行多个操作)和并行(使用多个线程同时执行多个CPU绑定操作)非常重要.

我注意到,在这项任务中,当前的文化在等待之后发生了变化.

如果您正在执行并行代码(即,ParallelTask.Run),那么您的操作应该受CPU限制,并且它们根本不应包含await任何内容.

避免服务器上的并行代码是个好主意.Task.Run在ASP.NET上几乎总是一个错误.

您的示例代码DownloadStringTaskAsync在后台线程(Task.Run)中执行异步work()并同步阻塞它(Wait).这没有任何意义.

如果您要进行异步工作,那么您可以使用async-ready原语,例如Task.WhenAll.以下代码将在ASP.NET上运行时保留文化,并将同时执行三次下载:

private async Task TestCultureAsync()
{
  Debug.WriteLine(CultureInfo.CurrentCulture);

  WebClient client = new WebClient();
  string s = await client.DownloadStringTaskAsync(new Uri("http://www.google.de"));

  Debug.WriteLine(CultureInfo.CurrentCulture);
}

var task1 = TestCultureAsync();
var task2 = TestCultureAsync();
var task3 = TestCultureAsync();
await Task.WhenAll(task1, task2, task3);
Run Code Online (Sandbox Code Playgroud)