System.threading.timer在循环中给出意想不到的结果

时间:2012-02-16 08:24:54

标签: c# multithreading delegates timer

我有一个创建计时器的循环。我有2个问题。

第一个问题是如何将命令作为“发射并忘记”运行,因此我可以调用它,它将在另一个线程中自行执行,它将自行回发给API。如果refresh属性设置为0,则会出现这种情况。

第二个是循环中的参数(check.key)在循环的下一次迭代中被替换。当委托触发方法时,它会传递替换值,而不是原始分配!!!

在下面的示例中,值被正确分配,但是当委托触发时,它总是使用参数“3”触发。

Dictionary _mydict充满了来自远程API的数据。

classtocall和methodtocall不会总是一样的。它们可以被多次调用 - 因为提供的密钥可以访问Test类中的更多参数。 Test类中的refresh属性定义了计时器时间。

我希望这是有道理的!

代码(快速模型,因为我的真实代码很大):

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 :(得分:0)

对于计时器部分:

您尚未显示您正在使用哪种Timer(有三种)。如果您正在使用System.Threading.TimerSystem.Timers.Timer,那么我希望它已经在线程池上执行...


对于错误的值部分:

你正在使用循环:

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

然后你用匿名方法捕获check

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

捕获变量 checkcheck的值在每次迭代时都会发生变化。您将始终看到最后一个值。这个很容易修复:

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创建委托,并将其传递给...

答案 1 :(得分:0)

开始一个新线程

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
}