计时器耗尽资源全球化

时间:2012-10-25 10:00:44

标签: c# .net c#-4.0 timer globalization

我有一个带有数据网格和计时器的表单。 我创建了一个资源CalculationSheet并在DUTCH中进行翻译   - 英国(默认)   - DUTCH

我用DUTCH语言启动应用程序。 当我选择新记录时,会弹出一个消息框。 它显示了正确的语言,荷兰语。 我也设置了计时器。

当计时器过去并再次显示消息框时,资源将以默认语言显示。

以下是该应用程序的主要入口点:

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
        new System.Globalization.CultureInfo("nl", true);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

这是回调代码:

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // shows in UK
    MessageBox.Show(Properties.Resources.CalculationSheet);
}

private void Form1_Load(object sender, EventArgs e)
{
    List<CalculationSheet> calculationSheets = new List<CalculationSheet>();

    calculationSheets.Add(new CalculationSheet("a"));
    calculationSheets.Add(new CalculationSheet("b"));
    calculationSheets.Add(new CalculationSheet("c"));

    this.dataGridView1.DataSource = calculationSheets;

    this.m_Timer = new System.Timers.Timer();
    this.m_Timer.Enabled = false;
    this.m_Timer.Interval = 5000;
    this.m_Timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);

    this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged);
}

private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
    // shows in DUTCH
    MessageBox.Show(Properties.Resources.CalculationSheet);
    this.m_Timer.Enabled = true;
}

1 个答案:

答案 0 :(得分:1)

System.Timers.Timer class的回调在单独的线程上执行回调。

手动设置Application.CurrentCulture property(或Thread.CurrentUICulture property)时,它不会将CultureInfo传递给其他创建的线程(CultureInfo不会随{跨越线程的{3}}这就是你看到这个的原因;回调在另一个线程上执行,CultureInfo未设置。

此测试用例显示CultureInfo.CurrentCulture未流向其他线程(因此不在Timer的回调中):

[TestMethod]
public void TestApplicationCurrentCultureInOtherThreads()
{
    // Create the timer.
    using (var t = new System.Timers.Timer(1000))
    {
        // The task completion source.
        var tcs = new TaskCompletionSource<object>();

        // Captured name.
        CultureInfo capturedCulture = null;

        // Set the current culture.
        // The default for the system needs to be something other
        // than "en-GB", mine is "en-US", which is why this
        // test passes.
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");

        // Copy t.
        var tCopy = t;

        // Event handler.
        t.Elapsed += (s, e) => {
            // Stop the timer.
            tCopy.Stop();

            // What's the captured name.
            capturedCulture = CultureInfo.CurrentCulture;

            // Complete the task.
            tcs.SetResult(null);
        };

        // Start.
        t.Start();

        // Wait.
        tcs.Task.Wait();

        // Compare.
        Assert.AreNotEqual(Thread.CurrentThread.CurrentUICulture, 
            capturedCulture);
    }
}

你的回调在显示MessageBox.Show方法时没有失败的原因是它不需要消息循环(它提供了它自己的一个)并且可以安全地从任何线程调用。

如果可能,我会将您需要的CultureInfo存储在应用程序的一个位置,然后将其传递给其他线程上需要的任何方法(ExecutionContext等方法)。 / p>

如果不可能,则必须在需要它的每个线程上设置Application.CurrentCulture。但要小心,如果在定时器回调线程上执行此操作,这些线程来自线程池,因此您永远不会知道线程池线程上的当前文化是什么(因为它没有重置)。

那就是说,如果你在这些回调中做UI工作,那么你应该通过调用String.Format将回调编组回UI线程(设置CultureInfo的地方)。或Post类上的Send方法(您可以存储UI线程上调用的SynchronizationContext中的值以从其他线程调用)。

您的Form1_Load方法会存储SynchronizationContext(在创建表单时设置):

private SynchronizationContext _synchronizationContext;

private void Form1_Load(object sender, EventArgs e)
{
    // Capture the context.
    _synchronizationContext = SynchronizationContext.Current;

    // Rest of code.
    ...
}

然后您的timer_Elapsed回调将如下所示:

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // Marshal back to the UI thread.
    _synchronizationContext.Send(s => {
        // Will now show in Dutch, as this call is taking place
        // on the UI thread.
        MessageBox.Show(Properties.Resources.CalculationSheet);
    }, null);    
}