【问题标题】:Wait for method to be finished C#等待方法完成 C#
【发布时间】:2022-08-07 17:13:59
【问题描述】:

我正在与外部设备 (PLC) 通信,他请求数据。

我有一个事件检查我的 PLC 中的值何时发生变化(例如 \"NeedNewPosition\"\"NeedNewBarValues\"

我想更改我的代码,它将一一处理它们。有时他似乎同时处理其中的两个。 (可能是因为一个比另一个需要更长的时间才能完成) 我读过一些关于异步方法和等待/任务等的内容,但对于这么简单的事情来说,这似乎需要做很多工作。

编码:

private void PLCValueChanged(object sender, EventArgs e)
{
    bool xDisplayValue = false;
    PLCVar plcvariable = (PLCVar)sender;
    string VarName = plcvariable.DisplayName;

    switch (VarName)
    {
        case \"NEEDNEWPOSITION\": //Writing required position to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            SearchNewPosition();
            OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, \"FALSE\");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, \"FALSE\");

            MagnetsInArea = GetMagnetsInWorkArea();
            SymbolsInArea = GetSymbolsInWorkArea();
            BarsInArea = GetBarsInWorkArea();
        }
        else
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, \"FALSE\");
        }
        break;

        case \"NEEDNEWBARVALUES\": //Writing Bar Values to PLC
        if (Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, \"FALSE\");
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, \"FALSE\");
            OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, \"FALSE\");

            SetFirstBarValues();

            OffsetsCalculated = false;

            StartVisualisation?.Invoke(this, null); //%M59
        }
        else //if (!Convert.ToBoolean(plcvariable.Value))
        {
            OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, \"FALSE\");
        }
        break;
  • 如果您发布代码,您能否发布一个完整的可编译块并正确格式化?谢谢。
  • 所以我猜PLCValueChanged 被其他东西触发了,你希望一个事件在另一个事件再次尝试调用它之前完成?
  • 对于您的问题:是的,活动能够以一种方式触发,即前一个仍在处理中时触发。如果发生这种情况,您需要考虑两件事: 1. 它是否经常发生?这意味着如果每一个触发的事件将与其前身重叠,那么无论您做什么,您都会遇到堆积。如果您只有窥视,那么构建管道可能就是您想要的。 2. 如果处理时间很长,您不希望在 Event-Thread 上处理事件。因此,无论如何,您可能想要构建某种管道。异步对1没有帮助。)
  • @DavidG 正确,因为NeedNewPosition 收到BarsInArea,他需要NeedNewBarValues 的信息。这就是为什么我希望第二个电话等待第一个电话完成。
  • @Fildor - 只有在使用多个线程时才会发生这种情况。在单线程模型中,您无法获得重叠线程。

标签: c#


【解决方案1】:

听起来您正在寻找Semaphore。就像like/wiki 说的:

信号量是一种变量或抽象数据类型,用于控制多个线程对公共资源的访问,并避免并发系统(如多任务操作系统)中的临界区问题。

IE。您可以使用信号量“阻塞”,直到资源再次可用。

在 C# 中有多种类型的信号量,但最简单的使用是 SemaphoreSlim

你可以为你的单例类实例定义一个静态的

private static readonly SemaphoreSlim _semaphore = new(1, 1);

1,1 表示:“1 可用,并且只能有 1”。

然后在您的代码中:

// take a semaphore, or wait until it is available
await _semaphore.WaitAsync(Timeout.Infinite, cancellationToken);
try
{
    [.. your work...]
}
finally
{
    // give the semaphore back
    _semaphore.Release();
}

注意,我在这里使用await,因为这意味着线程可以用于其他任务。它还将无限期地等待,直到信号量可用。阻止这种情况的方法是cancallationToken

【讨论】:

  • 我相信new Semaphore(1, 1)ManualResetEvent 基本相同。
  • @GoodNightNerdPride 我不这么认为。这看起来像旧的阻塞线程的东西。没有async 可以await
  • 它们都基于WaitHandleManualResetEvent 只是缺少一个异步接口(这在这种情况下可能没有帮助,因为 OPs 方法无论如何都会返回async void。我的意思只是:如果你正在创建new Semaphore(1, 1) 然后 dotnet 的线程锁定类型动物园提供了实现这种特殊情况的其他解决方案。
  • @GoodNightNerdPride 我不同意,你不想阻止线程有几个原因恕我直言。如果需要额外的逻辑来制作像ManualResetEvent async 这样的替代方案,我看不到附加值。顺便说一句,SemaphoreSemaphoreSlim 不同。看来你混淆了两者。
  • @GoodNightNerdPride 是的,基本上就是这样。更多信息here。信号量比苗条变体重得多。
【解决方案2】:

您可以使用AutoResetEvent 等待第一个事件的处理完成:

using System.Threading;
// ...

// declare lock as a static class member
private static AutoResetEvent barsInAreaLoaded = new AutoResetEvent(false);

private void PLCValueChanged(object sender, EventArgs e)
{
    // ...

    switch (VarName)
    {
        case "NEEDNEWPOSITION":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            BarsInArea = GetBarsInWorkArea();
            // signal waiting thread that bars are ready
            barsInAreaLoaded.Set();
        }
        // ...
        break;

        case "NEEDNEWBARVALUES":
        if (Convert.ToBoolean(plcvariable.Value))
        {
            // ...
            
            // block until bars are ready
            barsInAreaLoaded.WaitOne();
            SetFirstBarValues();
            
            // ...
        }
        // ...
        break;

请注意,这仅在您确定两个对应的NEEDNEWPOSITIONNEEDNEWBARVALUES 消息的处理重叠时才有效。如果其中一些消息实际上堆积起来,这不会解决您的问题,您应该考虑实施某种消息队列/管道。

【讨论】:

  • 请记住:事件处理程序应该执行迅速地.
  • @Fildor 我认为存在这种最佳实践是为了避免多线程问题,例如竞争条件,这正是 OP 面临的问题。所以是的,正确的答案应该是“实现消息队列”。
【解决方案3】:

不确定它是否可以解决您的问题,我不是 PLC 专家,但异步方法很容易实现。

我建议异步部分的一个简单示例。当然不完整,我建议您查看MSDN page 反正:

  1. 将函数声明为:private async void ...

  2. 运行一个异步函数,如:

    await Task.Run(() => 
    {
      // code to run async here 
    });
    

    声明await 是您正在寻找的(可能)!

【讨论】:

  • 让它async 不会解决他们遇到的问题。
  • @DavidG 是的,这是我回复他的。我只为异步部分提供了一个简单的实现,因为无论如何都是有问题的!顺便说一句,我猜他可能会等待 PLC 的回复(?)而不是 PLC exeprt
  • 不,如果 async 是错误的选择,那么你不应该告诉他们如何去做。他们遇到的问题是事件被多次触发,这与异步无关,并且不应该在问题中提及。
  • 由于他写了关于异步方法和实现的文章,我只是很难提供一个简单的实现。我写道我不是PLC专家,但既然他谈到了这一点,我只是提供解决方案......
  • “我不是 PLC 专家”这与 PLC 完全无关。它只是关于事件“堆积”时的事件处理。如果发生这种情况,使用异步实际上会使事情变得更糟,因为您的回击更少。
猜你喜欢
  • 2022-11-19
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多