简体   繁体   中英

Why doesn't async work on Elapsed event?

Hi I wanted to call on a subroutine from within a function however it didn't seem possible:

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

As a work around I tried this but when I try the code below I get the error "unexpected symbol void in class, struct, or interface member declaration"

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;
        }
}

Remove Async Keyword

You shouldn't have the async keyword on that function. The Elapsed timer event is not looking for an Async method.

It looks like you wanted to refer directly to the pre-existing private static void onEnergyTimer . I assume you got that code snippet using a decompiler (ilspy or other), and couldn't change that code.

Why async doesn't work on an event handler

Event handlers generally don't support/expect to be calling an async method. I'm not sure, but I think the one you are using uses the Windows Message pump to queue the message to action the Elapsed signal. The Windows Message pump is there so that all UI interactions occur on the Main Thread which owns the control handles.

The async pattern is not for Timers. Instead, if you did want an async equivelent of a delay, you would instead mark the Update function as Async, and then instead of using the Timer and Elapsed function, you would add the lines:

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

Mark third-party onEnergyTimer function as Public

Rather than implement your own accessible onEnergyTimer function, you could edit the third-party DLL (presumably), changing the function to public. There are many ways to do this, one is to request the third-party to update their accessibility of that function, another technical solution is to use the Mono Cecil library (I can go into more detail if required, just leave a comment) - this would work best if you have control over upgraded DLLs, and if you definitely want their version of that function for consistency.

You're trying to place your method signature outside of any class, where the compiler expects you to be declaring a class, struct, or interface. Move your method inside of your class.

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;
    }
}

This mistake can often happen because your curly-braces aren't matching properly. If you think your method is declared inside your class, inspect your braces to see if you've got a closing brace } where you didn't mean to.

I got some help with this and it turns out the issue was being caused by the editor (mono develop) and the version of .net that was being used by Unity (game engine). I wasn't able to change either of these factors so we used the code below as a work around:

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;
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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