简体   繁体   English

如何在不使用 Thread.Sleep 的情况下在循环内暂停?

[英]How to pause inside a loop without using Thread.Sleep?

using Thread.Sleep I manage to make a pause inside a loop but it has the disadvantage of freezing my program while the pause lasts.使用Thread.Sleep我设法在循环内暂停,但它的缺点是在暂停持续时冻结我的程序。 The purpose of my program is to start a loop when a button is clicked and to stop this loop when another button is clicked.我的程序的目的是在单击一个按钮时启动一个循环,并在单击另一个按钮时停止这个循环。 Here is the code I wrote:这是我写的代码:

private void startRPLoop_Click(object sender, EventArgs e)
{
    timer1.Interval = 1000;
    timer1.Enabled = true;
}

private void stopRPLoop_Click(object sender, EventArgs e)
{
    timer1.Interval = 1000;
    timer1.Enabled = false;
}

private void timer1_Tick(object sender, EventArgs e)
{
    if (timer1.Enabled == true)
    {
        GlobalRPValue = 500;
        WantedLevel = 1;
        Thread.Sleep(1000);
        WantedLevel = 0;
        Thread.Sleep(1000);
    }
    else
    {
        GlobalRPValue = 1;
        WantedLevel = 0;
    }
}

I thought of creating a Task so I could use await Task.Delay();我想创建一个Task ,这样我就可以使用await Task.Delay(); which will allow me to start the loop and make pauses without my program being suspended because of Thread.Sleep but I don't know how to go about it.这将允许我启动循环并暂停我的程序而不会因为Thread.Sleep而被暂停,但我不知道该怎么做。

I hope I have been precise enough because I am new to C# and thank you for your help :)我希望我足够精确,因为我是 C# 新手,感谢您的帮助:)

Your question is How to pause inside a loop without using Thread.Sleep?您的问题是如何在不使用 Thread.Sleep 的情况下在循环内暂停? . . You posted some sample code that uses System.Windows.Forms.Timer but when I worked it out using Timer it required more complicated code.您发布了一些使用System.Windows.Forms.Timer的示例代码,但是当我使用Timer解决它时,它需要更复杂的代码。 This answer proposes a simpler solution that (based on our conversation) does what you want without using Timer .这个答案提出了一个更简单的解决方案,(基于我们的对话)在不使用Timer的情况下做你想做的事。 It runs a loop when the button is clicked and toggles the WantedLevel between 0 and 1 once per second without freezing your UI.单击按钮时它会运行一个循环,并每秒一次在 0 和 1 之间切换WantedLevel ,而不会冻结您的 UI。

带有事件跟踪器的表单

The "Button" is actually a checkbox with Appearance = Button . “按钮”实际上是一个带有Appearance = Button的复选框。 Clicking it will toggle the checked state and when toggled on, it starts a loop.单击它会切换选中状态,并且当切换为打开时,它会启动一个循环。 First, the onTick method sets WantedLevel to 1 for a duration of 1 second before returning.首先, onTick方法在返回前将 WantedLevel 设置WantedLevel to 1持续 1 秒。 Then it will await an additional 1-second delay before repeating the process.然后它将在重复该过程之前await额外的 1 秒延迟。

CancellationTokenSource _cts = null;
private async void checkBoxLoop_CheckedChanged(object sender, EventArgs e)
{
    if(checkBoxLoop.Checked)
    {
        labelGlobalRPValue.Text = "GlobalRPValue=500";
        textBoxConsole.Text = $"{DateTime.Now.ToString(@"mm:ss")}: Start clicked{Environment.NewLine}";

        textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelGlobalRPValue.Text} {Environment.NewLine}");
        _cts = new CancellationTokenSource();
        while (checkBoxLoop.Checked)
        {
            try {
                await onTick(_cts.Token);
                await Task.Delay(1000, _cts.Token);
            }
            catch(TaskCanceledException)
            {
                break;
            }
        }
        ResetDefaults();
    }
    else
    {
        textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Stop clicked{Environment.NewLine}");
        _cts?.Cancel();
    }
}

The onTick handler is marked async which allows the Task.Delay to be awaited. onTick处理程序被标记为async ,它允许等待Task.Delay Other than that it's quite simple and tries to follow the essence of the handler you posted.除此之外,它非常简单,并尝试遵循您发布的处理程序的本质。

private async Task onTick(CancellationToken token)
{
    labelWantedLevel.Text = "WantedLevel=1";
    textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelWantedLevel.Text} {Environment.NewLine}");
    await Task.Delay(1000, token);

    labelWantedLevel.Text = "WantedLevel=0";
    textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelWantedLevel.Text} {Environment.NewLine}");
}

When the checkbox state toggles off, it cancels the current Task.Delay using the CancellationTokenSource which causes the loop to exit.当复选框状态关闭时,它会使用CancellationTokenSource取消当前的Task.Delay ,这会导致循环退出。 The ResetDefaults() method is called to restore default values for WantedLevel and GlobalRPValue . ResetDefaults()方法来恢复WantedLevelGlobalRPValue的默认值。

private void ResetDefaults()
{
    labelGlobalRPValue.Text = "GlobalRPValue=1";
    labelWantedLevel.Text = "WantedLevel=0";
    textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Cancelled (reset defaults) {Environment.NewLine}");
    textBoxConsole.AppendText($"{labelGlobalRPValue.Text} {Environment.NewLine}");
    textBoxConsole.AppendText($"{labelWantedLevel.Text} {Environment.NewLine}");
}

EDITS TO CONFORM TO ORIGINAL POST PER COMMENT编辑以符合原始帖子的评论

Handle Buttons手柄按钮

private bool _checkBoxLoop_Checked = false;
private void startRPLoop_Click(object sender, EventArgs e)
{
    _checkBoxLoop_Checked = true;
    checkBoxLoop_CheckedChanged(sender, e);
}

private void stopRPLoop_Click(object sender, EventArgs e)
{
    _checkBoxLoop_Checked = false;
    checkBoxLoop_CheckedChanged(sender, e);
}

Enable/Disable buttons for operational safety启用/禁用按钮以确保操作安全

private async void checkBoxLoop_CheckedChanged(object sender, EventArgs e)
{
    stopRPLoop.Enabled = _checkBoxLoop_Checked;  // Added
    startRPLoop.Enabled = !stopRPLoop.Enabled;   // Added
    if (_checkBoxLoop_Checked)  // use member variable instead of checkbox state
    {
        labelGlobalRPValue.Text = "GlobalRPValue=500";
        textBoxConsole.Text = $"{DateTime.Now.ToString(@"mm:ss")}: Start clicked{Environment.NewLine}";

        textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelGlobalRPValue.Text} {Environment.NewLine}");
        _cts = new CancellationTokenSource();
        while (_checkBoxLoop_Checked)
        {
            try {
                await onTick(_cts.Token);
                await Task.Delay(1000, _cts.Token);
            }
            catch(TaskCanceledException)
            {
                break;
            }
        }
        ResetDefaults();
    }
    else
    {
        textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Stop clicked{Environment.NewLine}");
        _cts?.Cancel();
    }
}

两个按钮版本

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM