C# Android rununuithread和onTimedEvent delay/lags(我试图显示一个倒计时计时器)

C# Android rununuithread和onTimedEvent delay/lags(我试图显示一个倒计时计时器),c#,android,xamarin,timer,xamarin.android,C#,Android,Xamarin,Timer,Xamarin.android,因此,我正在创建一个应用程序,显示从3到0的倒计时,用户可以单击重试,再次从3到0开始。我有一个tvTimer(文本视图)显示倒计时 代码工作正常,但有时会延迟,例如 当onCreate时,tvTimer.Text将显示3、2、1、0,但当我单击Retry按钮时,有时会显示3、2、2、0 你认为这是因为RunOnUIThread吗 这是我的密码 int CountSeconds; Button retry; TextView tvTimer; System.Tim

因此,我正在创建一个应用程序,显示从3到0的倒计时,用户可以单击重试,再次从3到0开始。我有一个tvTimer(文本视图)显示倒计时 代码工作正常,但有时会延迟,例如 当onCreate时,tvTimer.Text将显示3、2、1、0,但当我单击Retry按钮时,有时会显示3、2、2、0 你认为这是因为RunOnUIThread吗 这是我的密码

    int CountSeconds;
    Button retry;
    TextView tvTimer;
    System.Timers.Timer aTimer = new System.Timers.Timer();

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Pagee);
        tvTimer = FindViewById<TextView>(Resource.Id.tvTimer);
        retry = FindViewById<Button>(Resource.Id.retry);
        aTimer.Interval = 1000;
        aTimer.Elapsed += OnTimedEvent;

        play_timer();
        retry.Click += Retry_Click;
    }

    public void play_timer()
    {
         CountSeconds = 3; 
         aTimer.Start();
    }

    private void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        RunOnUiThread(() => { tvTimer.Text = Convert.ToString(CountSeconds );});

        CountSeconds  = CountSeconds  - 1;

        if (CountSeconds  == 0)
        {
            CountSeconds  = 3;
            aTimer.Stop();
        }

    private void Retry_Click(object sender, EventArgs e)
    {
             play_timer();
    }
int CountSeconds;
按钮重试;
文本视图tvTimer;
System.Timers.Timer aTimer=新的System.Timers.Timer();
创建时受保护的覆盖无效(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Pagee);
tvTimer=FindViewById(Resource.Id.tvTimer);
retry=findviewbyd(Resource.Id.retry);
a时间间隔=1000;
aTimer.appeased+=OnTimedEvent;
播放计时器();
重试。单击+=重试\u单击;
}
公共无效播放计时器()
{
计数秒=3;
aTimer.Start();
}
私有void OnTimedEvent(对象源,ElapsedEventArgs e)
{
RunOnUiThread(()=>{tvTimer.Text=Convert.ToString(CountSeconds);});
CountSeconds=CountSeconds-1;
如果(CountSeconds==0)
{
计数秒=3;
停止();
}
私有无效重试\u单击(对象发送方,事件参数e)
{
播放计时器();
}

谢谢!!!

如果您编写的代码在不同的线程上运行,就像这里一样

RunOnUiThread(() => { tvTimer.Text = Convert.ToString(CountSeconds );});

CountSeconds  = CountSeconds  - 1;
这意味着这两个部分中的任何一个可以先运行,或者它们甚至可以在多核/CPU系统上同时运行。例如,如果
CountSeconds
为3,则文本框中可能会显示3或2

为了确保事情发生的顺序更具再现性,您可以对这些事情重新排序,并将
RunOnUiThread
移动到方法的末尾。然后,您可以确保减法和检查零首先发生,并且仅在设置文本框之后发生

您还可以将调用中的整个逻辑移动到UI线程,使其在同一线程中同时发生,例如:

RunOnUiThread(() => {
  CountSeconds  = CountSeconds  - 1;

  tvTimer.Text = Convert.ToString(CountSeconds );

  if (CountSeconds  == 0)
  {
    CountSeconds  = 3;
    aTimer.Stop();
  }
});

如果您编写的代码在单独的线程上运行,如这里所示

RunOnUiThread(() => { tvTimer.Text = Convert.ToString(CountSeconds );});

CountSeconds  = CountSeconds  - 1;
这意味着这两个部分中的任何一个可以先运行,或者它们甚至可以在多核/CPU系统上同时运行。例如,如果
CountSeconds
为3,则文本框中可能会显示3或2

为了确保事情发生的顺序更具再现性,您可以对这些事情重新排序,并将
RunOnUiThread
移动到方法的末尾。然后,您可以确保减法和检查零首先发生,并且仅在设置文本框之后发生

您还可以将调用中的整个逻辑移动到UI线程,使其在同一线程中同时发生,例如:

RunOnUiThread(() => {
  CountSeconds  = CountSeconds  - 1;

  tvTimer.Text = Convert.ToString(CountSeconds );

  if (CountSeconds  == 0)
  {
    CountSeconds  = 3;
    aTimer.Stop();
  }
});

您在UI线程上运行代码,并在另一个线程中更改值。这将是一个竞争条件,可能发生在另一个线程之前。最好先进行减法,然后设置值,或者在同一UI线程中执行整个减法。@SamiKuhmonen Hi,感谢您的回复!很抱歉,我完全是新手,不理解减法first然后设置值,我应该先在哪里减法?谢谢!您在UI线程上运行代码,然后在另一个线程中更改值。这将是一个争用条件,其中一个可能发生在另一个线程之前。最好先减法,然后设置值,或者在同一UI线程中执行整个减法。@SamiKuhmonen嗨,谢谢您的回复!如此rry但是我完全是新手,不懂先减法然后设置值,我应该先在哪里减法?谢谢!非常感谢,这很有帮助。我还有最后一个问题。如果我在onTimedEvent运行时单击“重试”按钮会怎么样?它也在不同的线程上运行,对吗?假设它从3、2开始运行,然后我单击重试,这将再次将CountSeconds的值设置为3。会发生什么情况?我尝试了,结果看起来很混乱(数字更新延迟)@CarmenC事件运行的时间很短,所以这不重要。但如果希望计时器保持同步,则应停止计时器,重置值,然后再次启动计时器。从理论上讲,这种情况可能恰恰发生在计时器事件运行时,因此如果重要,可以始终向系统添加锁定,以确保当UI更新时,你什么都不做。非常感谢,这很有帮助。我遇到了最后一个问题。如果我在onTimedEvent运行时单击“重试”按钮会怎么样?它也在不同的线程上运行,对吗?那么假设它从3、2运行,然后单击“重试”,这将再次将CountSeconds的值设置为3。会发生什么?我试过了,结果看起来一团糟(数字更新延迟)@CarmenC事件运行的时间很短,所以这不重要。但如果希望计时器保持同步,则应停止计时器,重置值,然后再次启动计时器。从理论上讲,这种情况可能恰恰发生在计时器事件运行时,因此如果重要,可以始终向系统添加锁定,以确保当用户界面被更新时,你什么都不做。