System.threading.timer в цикле, дающий неожиданные результаты - PullRequest
0 голосов
/ 16 февраля 2012

У меня есть цикл, который создает таймеры. У меня с этим 2 проблемы.

Первая проблема заключается в том, как выполнить команду как «запустить и забыть», чтобы я мог ее вызвать, и она будет выполняться сама по себе в другом потоке, и сама отправит сообщение в API. Это тот случай, если для свойства refresh установлено значение 0.

Во-вторых, параметр в цикле (check.key) заменяется на следующей итерации цикла. Когда делегат запускает метод, ему передается замененное значение, а не назначенное оригинал !!!

В приведенном ниже примере значения назначены правильно, но когда делегат срабатывает, он всегда срабатывает с параметром "3".

Словарь _mydict заполнен данными из удаленного API.

Classtocall и methodtocall не всегда будут одинаковыми. Их можно вызывать более одного раза - так как предоставленный ключ предоставляет доступ к большему количеству параметров в классе Test. Свойство refresh в классе Test определяет время таймера.

Надеюсь, это имеет смысл!

Код (быстрый макет, так как мой настоящий код большой):

public class Test
{
    public string ClassToCall;
    public string MethodToCall;
    public string UniqueKey;
    public long Refresh;
    // more but removed for sample

    public Test(string MyClass, string MyMethod, string Key)
    {
        this.ClassToCall = MyClass;
        this.MethodToCall = MyMethod;
        this.UniqueKey = Key;
    }
}

class runme
{
    public static void runmefirst(string Key)
    {// Does some processing based on Key
    }

    public static void runmesecond(string Key)
    { // Does some processing based on Key
    }

    public static void runmelast(string Key)
    { // Does some processing based on Key
    }
}

class myclass
{   
    private static Timer[] timers;

    static void Main()
    {

        Dictionary<string, Test> _mydict = new Dictionary<string, Test>();

        _mydict.Add("1", new Test("runme", "runmefirst", "1"));
        _mydict.Add("2", new Test("runme", "runmesecond", "2"));
        _mydict.Add("3", new Test("runme", "runmelast", "3"));

        int i = 0;

        foreach (KeyValuePair<string,Test> check in _mydict)
        {

            Type t = Type.GetType(check.Value.ClassToCall);
            if (t != null)
            {
                MethodInfo mi = t.GetMethod(check.Value.MethodToCall);
                if (mi != null)
                {
                    if (check.Value.Refresh == 0)
                    {
                        mi.Invoke(null, new string[] { check.Key }); // << problem 1: need this fire and forget!
                    }
                    else
                    {
                        myclass.timers[i] = new Timer(
                            delegate { mi.Invoke(null, new string[] { check.Key }); }, // << problem 2: when called, check.Key is always last value in loop
                            null,
                            i * 1000,
                            check.Value.Refresh * 5000
                        );
                    }
                }
            }
            i+=1;
        }
    }
}

Спасибо

Ответы [ 2 ]

0 голосов
/ 16 февраля 2012

начать новую тему

System.Threading.Thread mythread = 
new System.Threading.Thread(new System.Threading.ThreadStart(MyFunction));
mythread .Start(); 

функция называется

private void MyFunction()
{
   if (InvokeRequired) 
   {
       this.Invoke(new MethodInvoker(MyFunction));
       return;
   }
   //DO YOUR STUFF
}
0 голосов
/ 16 февраля 2012

Для части таймеров:

Вы не показали, какой тип Timer вы используете (их три). Если вы используете System.Threading.Timer или System.Timers.Timer, то я ожидаю, что он уже выполняется в пуле потоков ...


Для неправильной части значения:

Вы используете цикл:

foreach (KeyValuePair<string,Test> check in _mydict)

и затем вы захватываете check анонимным способом:

delegate { mi.Invoke(null, new string[] { check.Key }); },

Это захват переменной check, и значение check меняется на каждой итерации. Вы всегда увидите последнее значение. Это легко исправить, хотя:

foreach (KeyValuePair<string,Test> check in _mydict)
{
    var copy = check;

    ...

    // Then later...
        delegate { mi.Invoke(null, new string[] { copy.Key }); },
}

На этот раз вы будете захватывать «новую» локальную переменную на каждой итерации, и значение этой локальной переменной не изменится.

Обратите внимание, что поведение в C # 5 изменится, чтобы ваш предыдущий код работал здесь.

С другой стороны, вы можете просто использовать Delegate.CreateDelegate для создания делегата непосредственно из MethodInfo и передать его в ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...