首页 > 解决方案 > 在线程中访问共享变量

问题描述

我正在使用下面的代码并在 foeach 循环中使用 Task.Run 以便每个循环都可以并行运行,因为我在 foreach 循环中有一些逻辑需要一些时间,但我面临的问题是 tContext.SecondNodeId = otherNode; 有时打印为 10 或 11 ,我想按前 10 后 11 的顺序打印,因为我还有一些代码,这些值对逻辑很重要。

class Program
{
    static void Main(string[] args)
    {
        TempData tContext = new TempData();
        var nodes = new List<int>() { 1, 2, 3, 4, 5, 6 };
        var otherNodes = new List<int> { 10, 11 };
        foreach (var node in nodes)
        {
            tContext.firstNodeId = node;
            List<Task> tasks = new List<Task>();
            foreach (var otherNode in otherNodes)
            {
                Task t = Task.Run(() =>
                {
                   
                     tContext.SecondNodeId = otherNode;
                    Console.WriteLine("First Node id is" + tContext.firstNodeId + "Second Node Id is " + tContext.SecondNodeId);
                    // I have some long running code which uses first node and second node id.
                });
                tasks.Add(t);
            }
            Task.WaitAll(tasks.ToArray());
        }
    }
}

请提出一种实现方法。

下面是输出。

在此处输入图像描述

标签: c#.netmultithreadingthread-safetythreadpool

解决方案


在您调用的内部循环内
Task t = Task.Run(() =>...);

它在另一个线程上运行 lambda。在这个 lambda 中,您正在设置:

tContext.SecondNodeId = otherNode;

然后从这个值中读取。因为您有多个线程写入此值,所以它可以在值 10 和 11 之间切换,具体取决于它们执行所需的时间。换句话说,内部循环可以在生成的任务执行之前执行多次迭代。这就是所谓的“竞争条件”。

因此,假设第一个内部循环迭代执行,并且任务到达设置点tContext.SecondNodeId =10。在这一行之后,完全有可能循环的第二次迭代刚刚完成并且衍生的任务会将其设置为 11。然后当第一个任务的其余部分执行时,它将使用值 11 而不是 10。

一种解决方法是使用局部变量:

var secondNodeId = otherNode;

并在你的 lambda 中使用它。这不能被其他任务访问,也不会改变。


推荐阅读