如何从异步上下文切换 MainThread 的 CurrentCulture?

Posted

技术标签:

【中文标题】如何从异步上下文切换 MainThread 的 CurrentCulture?【英文标题】:How to switch CurrentCulture of MainThread from async context? 【发布时间】:2020-09-07 14:28:59 【问题描述】:

我有一个旧 API,它以这种方式切换当前文化:

private void ChangeCulture(int lcid)
    
        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId] ChangeCulture Culture Start: Thread.CurrentThread.CurrentCulture");

        var newCulture = CultureInfo.GetCultureInfo(lcid);

        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId] Changing culture of currentThread to: newCulture");

        Thread.CurrentThread.CurrentCulture = newCulture;
        Thread.CurrentThread.CurrentUICulture = newCulture;

        // Old framework compatibility (not important for this example)
        CultureInfo.DefaultThreadCurrentCulture = newCulture;
        CultureInfo.DefaultThreadCurrentUICulture = newCulture;

        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId] ChangeCulture Culture End: Thread.CurrentThread.CurrentCulture");
    

以前,代码是从同步上下文中调用的。但是,由于现在需要从异步上下文中调用代码。喜欢这里:

private async void Button_Click(object sender, RoutedEventArgs e)
    
        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId]Culture Start: Thread.CurrentThread.CurrentCulture");
        if (this.currrentLcid == 1031)
        
            await Task.Delay(1000);
            this.ChangeCulture(1033);
            this.currrentLcid = 1033;
        
        else
        
            await Task.Delay(1000);
            this.ChangeCulture(1031);
            this.currrentLcid = 1031;
        

        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId]Culture End: Thread.CurrentThread.CurrentCulture");
    

输出:

[1]Culture Start: de-DE
[1] ChangeCulture Culture Start: de-DE
[1] Changing culture of currentThread to: en-US
[1] ChangeCulture Culture End: en-US
[1]Culture End: en-US

异步执行的文化流程。但它不会在全球范围内回流。如果我打电话:

    private void CheckCulture_Click(object sender, RoutedEventArgs e)
    
        Debug.WriteLine($"[Thread.CurrentThread.ManagedThreadId]CheckCulture_Click: Thread.CurrentThread.CurrentCulture");
    

输出:

[1]CheckCulture_Click: de-DE

一切都是主线程。 (ManagedThreadId=1)

.NET 框架 4.8

如何在不知道调用上下文的情况下全局切换当前文化?

【问题讨论】:

点击CheckCulture按钮时的预期结果是什么? @TheodorZoulias 是的,美国。这是最后设定的文化 【参考方案1】:

在 .NET Core 中,这些属性以 AsyncLocal<T> 的形式实现,简而言之,对它们的任何更新都只会影响当前的异步控制流,例如 async 方法。

根据您要完成的任务,您可以考虑创建和设置自己的静态CultureInfo 属性。

在 .NET Framework 4.6 及更高版本上,您可以在您的 App.config 中将 NoAsyncCurrentCulture 开关设置为 true 以恢复旧行为:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
  </startup>
  <runtime>
    <AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true"/>
  </runtime>
</configuration>

<AppContextSwitchOverrides> element

Retargeting Changes for Migration from .NET Framework 4.5 to 4.6

【讨论】:

我已经尝试过该标志,但通过代码设置它(在 Application_Startup 中)。不知何故,这不起作用。当我通过 App.config 设置它时,它似乎工作。谢谢。现在我必须考虑这个标志是否是正确的解决方案,但事实就是如此,有时很难做出决定。 如果我在 dispatcher.invokeAsync 中切换文化可能会更好关于这句话:受此更改影响的应用程序可以通过显式设置所需的 System.Globalization.CultureInfo.CurrentCulture 或System.Globalization.CultureInfo.CurrentUICulture 作为异步任务中的第一个操作

以上是关于如何从异步上下文切换 MainThread 的 CurrentCulture?的主要内容,如果未能解决你的问题,请参考以下文章

Python 中的进程线程协程同步异步回调

如何从不同的线程访问 MainThread 元素? [复制]

异步编程的优势和难点

C++11实现半同步半异步的线程池

C#异步异步多线程的本质,上下文流转和同步

「理解C++20协程原理」从Linux线程线程与异步编程协程与异步