【问题标题】:MessageBox and while loop C#MessageBox 和 while 循环 C#
【发布时间】:2019-11-27 16:41:55
【问题描述】:

我正在修改现有的 C# 代码以驾驶活塞。每 30 毫秒,我会通过一个事件直接反馈活塞的位置。该值存储在我用来获取活塞当前位置的全局变量中。

我想要实现的目标:对于给定的距离输入 (A->C),我希望活塞全速行驶 95% 的距离 (A->B),然后再慢一点剩余 5% (B->C)。

我可以访问定义活塞速度和目的地的命令:pos(velocity,destination)。

但是,如果我编写该代码:

pos(fullSpeed,B);
pos(reducedSpeed, C);

活塞直接从全速转到减速

我尝试使用 while 循环将活塞的当前位置与目标位置进行比较,但是,在进入 while 循环后,存储活塞位置的变量不再更新。

但是,我注意到通过在其间放置一个 MessageBox,位置值会不断更新,我只需单击“确定”即可启动第二个命令。

pos(fullSpeed,B);
MessageBox.show("Wait");
pos(reducedSpeed, C);

我想知道为什么“while”循环会停止位置变量的更新,但 MessageBox 不会。我的意思是,只要我不单击“确定”按钮,该框就会阻止我做任何事情,这对我来说类似于 while 循环行为。我还有其他方法可以代替 MessageBox 吗?

我对 C# 知之甚少,也没有任何支持。我试图查看文档,但没有找到答案(我可能错过了)。任何线索都非常受欢迎。

编辑:我没有该代码的文档,几乎没有评论。这是我收集的(真的希望它有所帮助):

要移动活塞,调用函数:

MyEdc.Move.Pos(control, speed, destination, ref MyTan);

control 只是定义我们驾驶的内容(距离或负载,它是一个枚举),我不知道 MyTan 做了什么。我只知道 MyEdc.Move.Pos 返回错误代码。

如果我查看“pos”的定义,我会被重定向到类

public DoPEmove Move;

其中包括:

public DoPE.ERR Pos(DoPE.CTRL MoveCtrl, double Speed, double Destination, ref short Tan);

DoPE.ERR 也是一个类型枚举。但是,我无法达到名为“Pos”的函数的定义。是否包含在 .dll 中?

以下是允许我访问活塞位置的代码(没有全局变量):

private int OnData(ref DoPE.OnData Data, object Parameter)
    {
      if (Data.DoPError == DoPE.ERR.NOERROR)
      {
        DoPE.Data Sample = Data.Data;
        Int32 Time = Environment.TickCount;
        if ((Time - LastTime) >= 300 /*ms*/)
        {
          LastTime = Time;
          string text;
          text = String.Format("{0}", Sample.Time.ToString("0.000"));
          guiTime.Text = text;
          text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S].ToString("0.000"));
          guiPosition.Text = text;
          text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F].ToString("0.000"));
          guiLoad.Text = text;
          text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E].ToString("0.000"));
          guiExtension.Text = text;
        }
      }
      return 0;
    }

调用方法

MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData);

我意识到我对软件的运作方式知之甚少,这对您来说是多么令人沮丧。如果您认为这是一个失败的原因,没问题,我会尝试 Timothy Jannace 解决方案,如果它对我没有帮助,我会坚持使用 MessageBox 解决方案。我只是想知道为什么 MessageBox 允许我实现我的 objectif,但 while 循环没有,以及如何在我的优势中使用它。

【问题讨论】:

  • pos()的代码是什么?那是异步方法吗?您试图在文档中找到什么?
  • pos一无所知,我们真的无话可说。显然,这是一个异步调用。是否有返回值(或在到达位置时通知您的重载)?职位更新是如何设计的?显然,它与您的while 在同一线程上运行。因此,while 只是阻止了该更新。同样,如果对图书馆一无所知,就不可能说出有用的东西。
  • 请提供pos()的代码

标签: c# while-loop messagebox


【解决方案1】:

我尝试使用while循环来比较当前位置 然而,在进入时,带有目标目的地的活塞 循环,存储活塞位置的变量不会更新 没有了。

当您处于while 循环中时,您的应用无法再接收和处理反馈事件

一种可能的解决方案是像这样使用async/await

private const int fullSpeed = 1;
private const int reducedSpeed = 2;

private int currentPistonPositon = 0; // global var updated by event as you described

private async void button1_Click(object sender, EventArgs e)
{
    int B = 50;
    int C = 75;

    pos(fullSpeed, B);

    await Task.Run(() =>
        {   // pick one below?

            // assumes that "B" and "currentPistonPosition" can actually be EXACTLY the same value
            while (currentPistonPositon != B) 
            {
                System.Threading.Thread.Sleep(25);
            }

            // if this isn't the case, then  perhaps when it reaches a certain threshold distance?
            while (Math.Abs(currentPistonPositon - B) > 0.10)
            {
                System.Threading.Thread.Sleep(25);
            }
        });

    pos(reducedSpeed, C);
}

注意button1_Click 方法签名已用async 标记。由于await,代码将等待任务内的while 循环完成,同时仍在处理事件 消息。只有这样,它才会进入第二个pos() 调用。

感谢您的回答!它就像一个魅力! (很好的赶上 精确值)。我学到了很多,我确信 async/await 组合是 将来会很有用! – 马克西姆

如果效果不错,那么您可能需要考虑重构代码并制作自己的“转到位置”方法,如下所示:

private void button1_Click(object sender, EventArgs e)
{
    int B = 50;
    int C = 75;

    GotoPosition(fullSpeed, B);
    GotoPosition(reducedSpeed, C);
}

private async void GotoPosition(int speed, int position)
{
    pos(speed, position);

    await Task.Run(() =>
    {
        while (Math.Abs(currentPistonPositon - position) > 0.10)
        {
            System.Threading.Thread.Sleep(25);
        }
    });
}

可读性会大大提高。

您甚至可以更花哨,在 while 循环中引入 超时 概念。现在您的代码可以执行如下操作:

private void button1_Click(object sender, EventArgs e)
{
    int B = 50;
    int C = 75;

    if (GotoPosition(fullSpeed, B, TimeSpan.FromMilliseconds(750)).Result)
    {
        if (GotoPosition(reducedSpeed, C, TimeSpan.FromMilliseconds(1500)).Result)
        {
            // ... we successfully went to B at fullSpeed, then to C at reducedSpeed ...
        }
        else
        {
            MessageBox.Show("Piston Timed Out");
        }
    }
    else
    {
        MessageBox.Show("Piston Timed Out");
    }

}

private async Task<bool> GotoPosition(int speed, int position, TimeSpan timeOut)
{
    pos(speed, position); // call the async API

    // wait for the position to be reached, or the timeout to occur
    bool success = true; // assume we have succeeded until proven otherwise
    DateTime dt = DateTime.Now.Add(timeOut); // set our timeout DateTime in the future
    await Task.Run(() =>
    {
        System.Threading.Thread.Sleep(50); // give the piston a chance to update maybe once before checking?
        while (Math.Abs(currentPistonPositon - position) > 0.10) // see if the piston has reached our target position
        {
            if (DateTime.Now > dt) // did we move past our timeout DateTime?
            {
                success = false;
                break;
            }
            System.Threading.Thread.Sleep(25); // very small sleep to reduce CPU usage
        }
    });
    return success;
}

【讨论】:

  • 感谢您的回答!它就像一个魅力! (准确的值很好)。我学到了很多,我相信 async/await 组合在未来会非常有用!
  • 活塞是否通过事件一直报告其位置,还是仅在响应pos() 调用时报告其位置,然后仅在停止移动之前报告?
  • 非常感谢您的编辑,我今天仍然会实施!
【解决方案2】:

如果您使用事件,您可能会遇到并发问题。尤其是每 30 毫秒引发一次事件!

处理并发的一个非常简单的方法是使用一个锁对象来防止不同的线程同时使用竞争资源:

class MyEventHandler
{
    private object _lockObject;

    MyEventHandler()
    {
        _lockObject = new object();
    }

    public int MyContestedResource { get; }

    public void HandleEvent( object sender, MyEvent event )
    {
        lock ( _lockObject )
        {
            // do stuff with event here
            MyContestedResource++;
        }
    }
}

请记住,这非常简单,绝不是在所有情况下都完美无缺。如果您提供有关如何引发事件以及您如何处理这些事件的更多信息,人们将能够提供更多帮助。

编辑:

使用您为 Pos 方法发布的签名,我能够找到有关您正在使用的库的文档:https://www.academia.edu/24938060/Do_PE

转到定义时只看到方法签名的原因是该库已编译为 dll。实际上,无论如何查看代码可能并没有那么有用,因为它看起来像库是原生(c 或 c++)代码的 C# 包装器。

无论如何,我希望文档对您有所帮助。如果你看第 20 页,有一些关于运动的指示。这对新程序员来说将是一个挑战,但你可以做到。我建议您避免使用事件处理程序来驱动您的逻辑,而是坚持使用 同步 版本的命令。使用同步命令,您的代码应该以与其读取相同的方式运行。

【讨论】:

  • 感谢蒂莫西的回答!如果我了解正在发生的事情是您的代码,我似乎无法正确实现它(这里的技能非常有限,抱歉)。我不太了解这里发生的很多事情(尤其是所有事件以及它们被调用的位置)这一事实无济于事。
  • 非常感谢您对我的问题的意见。我一直在找这个文档,但是连机器制造商都没有,所以我没有太努力地在网上寻找它。我相信这会有所帮助,我会听从您的建议,并尽量坚持使用同步命令!
【解决方案3】:

我相信您想要做的是添加一个调用:

Application.DoEvents();

这将允许您的应用程序处理发布的消息(事件),从而允许更新全局变量。

我只是想知道为什么 MessageBox 允许我实现我的 objectif,但 while 循环却没有,以及如何在这里发挥我的优势。

之所以有效,是因为您让 WndProc 有机会处理已发送到应用程序的事件。这不是对MessageBox.Show(); 的调用的预期功能,但这是一个结果。您可以通过调用Application.DoEvents(); 来执行相同的操作,而不会中断消息框。

【讨论】:

  • 非常感谢您的解释!只需添加Application.DoEvents() 即可!我会选择@Idle_Mind 解决方案,因为它使代码更易于阅读,但非常感谢您花时间向我解释发生了什么并提供解决方案。
猜你喜欢
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2020-03-30
  • 2015-01-13
  • 2010-10-07
  • 2014-11-22
相关资源
最近更新 更多