在C#中多个线程调用同一方法

3
我有以下 C# 代码片段,其中模拟了我的问题。在此程序中,我有一个服务函数调用 ReadRooms 方法。 现在我在不同的线程上调用服务方法。我原本期望 ServiceCall 和 ReadRooms 方法会平等地触发,但是我得到了下面的结果,这是不正确的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        public static void ReadRooms(int i)
        {
            Console.WriteLine("Reading Room::" + i);
            Thread.Sleep(2000);
        }
        public static void CallService(int i)
        {
            Console.WriteLine("ServiceCall::" + i);
            ReadRooms(i);

        }
        static void Main(string[] args)
        {
            Thread[] ts = new Thread[4];
            for (int i = 0; i < 4; i++)
            {
                ts[i] = new Thread(() =>
                    {
                        int temp = i;
                        CallService(temp);


                    });
                ts[i].Start();
            }
            for (int i = 0; i < 4; i++)
            {
                ts[i].Join();
            }
            Console.WriteLine("done");
            Console.Read();

        }
    }
}

尝试在你调用的方法上添加 [MethodImpl(MethodImplOptions.Synchronized)],这类似于 Java 中的 synchronized 关键字。 - ddavison
@sircapsalot:我尝试了这个,但仍然得到错误的结果。 - santosh singh
3个回答

8
你仍在“捕获循环变量”。你创建了一个temp,但太晚了,当i已经被捕获时。
尝试这样做:
for (int i = 0; i < 4; i++)
{
   int temp = i;              // outside the lambda
   ts[i] = new Thread(() =>
   {
        //int temp = i;       // not here
        CallService(temp);
   });
   ts[i].Start();
}

3

你的线程操作在变量 i 上进行了闭包,而不是它当前的值。因此,在线程读取 i 和 for 循环中递增之间存在竞争条件。你可以将其作为参数传递:

ts[i] = new Thread(index =>
{
    CallService((int)index);
});
ts[i].Start(i);

或者,您可以将temp的副本移动到循环内部,而不是线程操作中:

for (int i = 0; i < 4; i++)
{
    int temp = i;
    ts[i] = new Thread(() =>
    {
        CallService(temp);
    });
    ts[i].Start();
}

3
没有i的竞赛。 - H H
@HenkHolterman - 如果在线程读取值之前增加了 i,那么不同的线程就可以看到相同的值。这不是竞态条件吗? - Lee
1
这不是我们通常所说的竞争,因为 i 最初并不打算共享。这有点正确,但不是核心问题。而且你不能用 lock 来解决它。 - H H

3

您应该放置这行代码

int temp = i;

创建线程之前

for (int i = 0; i < 4; i++)
{
    int temp = i;
    ts[i] = new Thread(() => CallService(temp));
    ts[i].Start();
}

这样你将创建一个本地的i副本,该副本将被lambda表达式使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接