为什么线程得到demo.names为null?

时间:2016-07-11 12:32:03

标签: c# multithreading

这是我的代码:

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 200; i++)
        {
            Console.WriteLine("---------" + i.ToString());
            Demo.TestError();
        }
    }

    public class Demo
    {
        public Demo() { }

        public Demo(int i) { index = i; }

        public static void TestError()
        {
            List<Thread> threads = new List<Thread>();
            Demo demo = null;
            for (int i = 0; i < 1000; i++)
            {
                if (i % 10 == 0)
                {
                    demo = new Demo(i);
                }
                #region code1
                Thread t = new Thread(() =>
                {
                    demo.SetName();
                    var names = demo.names;
                    string msg = null;
                    if (names == null)
                    {
                        msg = demo.index.ToString() + " -" + Thread.CurrentThread.ManagedThreadId.ToString() + "-is null";
                        Console.Write(msg);
                    }
                    else if (names == null || names.Count <= 0)
                    {
                        msg = demo.index.ToString() + " -" + Thread.CurrentThread.ManagedThreadId.ToString() + "-is zero";
                        Console.Write(msg);
                    }
                    else
                    {
                        msg = demo.index.ToString() + " -" + Thread.CurrentThread.ManagedThreadId.ToString() + "-is ok" + "-" + string.Join(",", names);
                    }
                });
                t.Start();
                #endregion
                threads.Add(t);
            }
            for (int i = 0; i < threads.Count; i++)
            {
                threads[i].Join();
            }
        }

        public List<string> names;

        public int index;

        public object lockObj = new object();

        public void SetName()
        {
            if (names == null)
            {
                lock (lockObj)
                {
                    if (names == null)
                    {
                        var tnames = new List<string>();
                        tnames.Add("a");
                        System.Threading.Interlocked.CompareExchange(ref names, tnames, null);
                    }
                }
            }
        }
    }

运行它,它显示(部分):

  

--------- 60 750 -747-无效--------- 61   --------- 62

     

...

     

--------- 95 960 -5174-null null --------- 96   --------- 97

     

...

     

--------- 101 580 -3591-is null --------- 102   --------- 103

     

...

     

--------- 112 720 -2193 - 为空--------- 113   --------- 114

     

...

     

--------- 123 50 -2790-is null --------- 124   --------- 125

     

...

     

--------- 133 420 -1237-为空--------- 134   --------- 135

     

...

将code1更改为code2:

                #region code2
                Thread t = new Thread((obj) =>
                {
                    var d = obj as Demo;
                    d.SetName();
                    var names = d.names;
                    ...//the same as above
                });
                t.Start(demo);
                #endregion

它运行成功!那么

之间的区别是什么
public Thread(ThreadStart start);

public Thread(ParameterizedThreadStart start);

?为什么demo.names为空或其计数为零?

2 个答案:

答案 0 :(得分:1)

他们的唯一不同之处在于ParameterizedThreadStart获取了一个对象作为输入,但ThreadStart并不是那么简单。

那么为什么其中一个有效,另一个无效呢?这是因为你的范围可变。使用ParameterizedThreadStart后,它会将demo变量作为参数传递,因此当for循环迭代到下一次运行时,它不会更改其引用,而在第一个代码中您正在使用相同的对象并在所有线程之间共享它。

主要问题是您已在demo块之外定义了for变量。因此,当您更改变量的引用(演示)时,正在发生竞争条件在循环中轻快,但它仍然在你的线程中使用。

Demo demo = null;
for (int i = 0; i < 1000; i++)
{
  if (i % 10 == 0)
  {
     demo = new Demo(i);
  }
  ...
}

如果您将代码更改为此代码,它应该得到修复并适用于两种情况:

for (int i = 0; i < 1000; i++)
{
  Demo demo = null;
  if (i % 10 == 0)
  {
     demo = new Demo(i);
  }
  ...
}

答案 1 :(得分:0)

我知道为什么code1在

中获取demo.names == null
                if (i % 10 == 0)
                {
                    demo = new Demo(i);
                }
                #region code1
                Thread t = new Thread(() =>
                {
                    demo.SetName();
                    var names = demo.names;

当thread1运行到demo.SetName()时,它将demo.names设置为null,但是thread2运行到demo = new Demo(i),它将demo.names设置为null,然后var names = demo.names它获取demo.names null,但现在demo是与demo.SetName()的不同实例。 @akazemis谢谢你!