使用C#进行奇怪的线程处理

时间:2011-11-09 15:31:05

标签: c# multithreading

我遇到了C#线程的奇怪问题。

这是我的示例程序,使用线程“激活”agentList中每个代理的Print()函数。

class Program {

    static void Main(string[] args) {

        List<Agent> agentList = new List<Agent>();

        agentList.Add(new Agent("lion"));
        agentList.Add(new Agent("cat"));
        agentList.Add(new Agent("dog"));
        agentList.Add(new Agent("bird"));

        foreach (var agent in agentList) {
            new Thread(() => agent.Print()).Start();
        }

        Console.ReadLine();
    }
}

class Agent {
    public string Name { get; set; }

    public Agent(string name) {
        this.Name = name;
    }

    public void Print() {
        Console.WriteLine("Agent {0} is called", this.Name);
    }
}

以下是我运行上述程序时的结果:

Agent cat is called
Agent dog is called
Agent bird is called
Agent bird is called

但我所期待的是包含所有4个代理商的东西,比如

Agent lion is called
Agent cat is called
Agent dog is called
Agent bird is called

最令人惊奇的是,如果我在foreach之外调用线程,它就可以了!

class Program {
    static void Main(string[] args) {
        List<Agent> agentList = new List<Agent>();

        agentList.Add(new Agent("leecom"));
        agentList.Add(new Agent("huanlv"));
        agentList.Add(new Agent("peter"));
        agentList.Add(new Agent("steve"));

        new Thread(() => agentList[0].Print()).Start();
        new Thread(() => agentList[1].Print()).Start();
        new Thread(() => agentList[2].Print()).Start();
        new Thread(() => agentList[3].Print()).Start();


        Console.ReadLine();
    }
}

上述代码的结果正是我所期望的。那么这里的问题是什么?

5 个答案:

答案 0 :(得分:9)

你有什么关闭。你正在关闭foreach循环中的变量。发生的事情是变量在线程启动之前被覆盖,因此您有两次具有相同值的迭代。

简单的解决方法是在使用之前捕获foreach循环中的值:

foreach(var a in agentList)
{
    var agent = a;
    new Thread(() => agent.Print()).Start();
}

答案 1 :(得分:2)

您正在关闭中捕获agent,这可能会导致多线程出现问题。首先分配给局部变量:

    foreach (var agent in agentList) {
        var temp = agent;
        new Thread(() => temp.Print()).Start();
    }

答案 2 :(得分:1)

像这样改变你的foreach循环:

foreach (var agent in agentList) 
{
    var agent1 = agent;
    new Thread(() => agent1.Print()).Start();         
} 

将值复制到局部变量(看起来有点愚蠢)可以避免使用可能在运行时更改的变量的线程。

答案 3 :(得分:0)

这是因为var代理可以在线程中具有相同的引用。 这就是为什么不推荐你早期的方法。

答案 4 :(得分:-1)

如果不使用某种线程同步(如ManualResetEvent),则无法保证线程的处理顺序。如果你想按顺序执行多个步骤,我建议你将工作捆绑起来,然后在一个后台线程中执行所有这些步骤。

我喜欢BackgroundWorker对象:

List<Agent> agentList = new List<Agent>();

agentList.Add(new Agent("leecom"));
agentList.Add(new Agent("huanlv"));
agentList.Add(new Agent("peter"));
agentList.Add(new Agent("steve"));

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
    foreach (var item in agentList)
    {
        item.Print();
    }
};

worker.RunWorkerAsync();