【问题标题】:Why doesn't async work on Elapsed event?为什么异步对 Elapsed 事件不起作用?
【发布时间】:2015-06-05 21:47:31
【问题描述】:

您好,我想从函数中调用子例程,但似乎不可能:

private static void onEnergyTimer(object source, ElapsedEventArgs e)
    {
        if (energy < pastEnergy - 20) {
            StartCoroutine(SendData());}
}

作为一种解决方法,我尝试了这个,但是当我尝试下面的代码时,我收到错误“类、结构或接口成员声明中的意外符号无效”

using System;
using System.Collections;
using System.Collections.Generic;
using System.Timers; 
using UnityEngine;
public class StickTimerBetaCoroutine2 : MonoBehaviour
{
        public static float energy = 50.0f;
        private static float pastEnergy = energy;
        public void Update ()
        {
                System.Timers.Timer energyTimer = new System.Timers.Timer ();
                energyTimer.Elapsed += new ElapsedEventHandler (onEnergyTimer);
                energyTimer.Interval = 3000;
                energyTimer.Enabled = true;
                {
                        if (JoeysInputStick.flex <= 0) {
                                energy -= 8 * (Time.deltaTime);
                        } else if (JoeysInputStick.flex >= 1) {
                                energy += 2;
                        } else if (JoeysInputStick.flex >= 2) {
                                energy += 5;
                        } else if (JoeysInputStick.flex >= 3) {
                                energy += 7;
                        } else if (JoeysInputStick.flex >= 4) {
                                energy += 9;
                        } else if (JoeysInputStick.flex >= 5) {
                                energy += 11;
                        } else if (JoeysInputStick.flex >= 6) {
                                energy += 13;
                        }
                }
                energy = Mathf.Clamp (energy, 0.0F, 600.0F); 
                RectTransform rTrans = (RectTransform)transform.GetComponent<RectTransform> ();
                rTrans.sizeDelta = new Vector2 (200, energy);   
        }   
        private async void onEnergyTimer (object source, ElapsedEventArgs e)
        {
                if (energy < pastEnergy - 20) {
                        JoeysInputStick.SendTensOn ();
                        await Task.Delay (800);
                        JoeysInputStick.SendTensOff ();
                }
                pastEnergy = energy;
        }
}

【问题讨论】:

  • energypastEnergy 是什么?还是StartCoroutine()SendData()
  • @FrédéricHamidi:我认为这些问题与他遇到的错误无关。
  • @StriplingWarrior,您的回答很可能是正确的(仍然需要提问者的确认),但它只解决了问题的第二部分。我的(补充)评论是关于问题的第一部分,因为我在这里闻到了 XY 问题:)

标签: c# asynchronous void


【解决方案1】:

移除异步关键字

您不应该在该函数上使用 async 关键字。 Elapsed timer 事件不是在寻找 Async 方法。

您似乎想直接引用预先存在的private static void onEnergyTimer。我假设您使用反编译器(ilspy 或其他)获得了该代码 sn-p,并且无法更改该代码。

为什么异步在事件处理程序上不起作用

事件处理程序通常不支持/不期望调用异步方法。我不确定,但我认为您使用的那个使用 Windows 消息泵将消息排队以操作 Elapsed 信号。存在 Windows 消息泵,因此所有 UI 交互都发生在拥有控制句柄的主线程上。

异步模式不适用于计时器。相反,如果您确实想要延迟的异步等效项,则可以将 Update 函数标记为 Async,然后不使用 Timer 和 Elapsed 函数,而是添加以下行:

            await Task.Delay(3000); //Here's your delay
            if (energy < pastEnergy - 20) {
                    JoeysInputStick.SendTensOn ();
                    await Task.Delay (800);
                    JoeysInputStick.SendTensOff ();
            }
            pastEnergy = energy;

将第三方 onEnergyTimer 函数标记为公开

与其实现您自己的可访问的 onEnergyTimer 函数,不如编辑第三方 DLL(大概),将函数更改为 public。有很多方法可以做到这一点,一种是请求第三方更新他们对该功能的可访问性,另一种技术解决方案是使用 Mono Cecil 库(如果需要,我可以更详细地介绍,请发表评论) -如果您可以控制升级的 DLL,并且您肯定希望他们的该函数版本保持一致性,这将是最好的。

【讨论】:

    【解决方案2】:

    您试图将您的方法签名放在任何类之外,编译器希望您在其中声明一个类、结构或接口。将你的方法移动到你的类中。

    public class MyClass
    {
        ...
    
        private async void onEnergyTimer(object source, ElapsedEventArgs e)
        {
            if (energy < pastEnergy - 20) {
                JoeysInputStick.SendTensOn();
                await Task.Delay(800);
                JoeysInputStick.SendTensOff();
            }
            pastEnergy = energy;
        }
    }
    

    这个错误经常发生,因为你的花括号没有正确匹配。如果您认为您的方法是在您的类中声明的,请检查您的大括号,看看您是否有一个右大括号 },而您并不打算这样做。

    【讨论】:

    • 感谢您的帮助,但不幸的是,这似乎不是问题(据我所知)。
    • 我已将整个代码添加到上面的原始查询中。任何帮助都会很棒。
    • @joeyc:您仍然收到最初报告的错误消息,还是现在的错误消息不同?
    【解决方案3】:

    我得到了一些帮助,结果发现问题是由编辑器(单声道开发)和 Unity 使用的 .net 版本(游戏引擎)引起的。我无法更改其中任何一个因素,因此我们使用下面的代码作为解决方法:

    private Queue energyQueue = new Queue();
            private bool tensInAction = false;
            private void onEnergyTimer (object source, ElapsedEventArgs e)
            {
                float maxEnergy = 0;
                foreach (System.Object reading in (IEnumerable) energyQueue) {
                    if ((float) reading > maxEnergy) {
                        maxEnergy = (float) reading;
                    }
                }
    
                if (energy < maxEnergy - 20 && !tensInAction) {
                    energyQueue.Clear();
                    tensInAction = true;
                    JoeysInputStick.SendTensOn ();
                    System.Timers.Timer tensTimer = new System.Timers.Timer ();
                    tensTimer.Elapsed += new ElapsedEventHandler (onTensTimer);
                    tensTimer.Interval = 800;
                    tensTimer.Enabled = true;
                }
    
                energyQueue.Enqueue(energy);
                if (energyQueue.Count > 12) {
                    energyQueue.Dequeue();
                }
    
            }
    
        private void onTensTimer (object source, ElapsedEventArgs e) {
                JoeysInputStick.SendTensOff ();
                tensInAction = false;
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      相关资源
      最近更新 更多