c#lock未按预期工作

时间:2015-02-07 23:29:26

标签: c# .net multithreading locking interlocked

此课程使用lockInterlocked

increaseCount.with_lock.Run();increaseCount.with_interlock.Run();都打印在96-100之间。

我期待他们两个人总是打印100.我犯了什么错误?

public static class increaseCount {
    public static int counter = 0;
    public static readonly object myLock = new object();
    public static class with_lock {
        public static void Run() {
            List<Thread> pool = new List<Thread>();
            for(int i = 0; i < 100; i++) {
                pool.Add(new Thread(f));
            }
            Parallel.ForEach(pool, x => x.Start());
            Console.WriteLine(counter); //should print 100
        }

        static void f() {
            lock(myLock) {
                counter++;
            }
        }
    }

    public static class with_interlock {
        public static void Run() {
            List<Thread> pool = new List<Thread>();
            for(int i = 0; i < 100; i++) {
                pool.Add(new Thread(f));
            }
            Parallel.ForEach(pool, x => x.Start());
            Console.WriteLine(counter);//should print 100
        }

        static void f() {
            Interlocked.Add(ref counter, 1);
        }
    }
}

3 个答案:

答案 0 :(得分:4)

在这两种情况下,您都可以启动线程,但不要等到它们完成,因此在打印结果并关闭应用程序之前,您没有达到100。

如果在启动所有线程之后,您将等待所有这些线程完成Thread.Join,您将始终获得正确的结果:

List<Thread> pool = new List<Thread>();
for (int i = 0; i < 100; i++)
{
    pool.Add(new Thread(f));
}

Parallel.ForEach(pool, x => x.Start());
foreach (var thread in pool)
{
    thread.Join();
}

Console.WriteLine(counter);

注意:这似乎是某种类型的测试,但您应该知道在单个lock上阻塞多个线程是一种巨大的资源浪费。

答案 1 :(得分:1)

我相信这是因为你的Parallel.Foreach调用只是调用pool中所有线程的start,但它们不一定在循环结束并调用Console.WriteLine时完成。如果您要在Thread.Sleep(5000); // 5s sleep之前插入Console.WriteLine或类似内容,则可能会打印出您期望的内容。

答案 2 :(得分:1)

你的代码很好。唯一的问题是你的期望。基本上,并非所有100个线程都可以运行,直到显示计数器。尝试在Console.WriteLine(计数器)之前放置一个Thread.Sleep(1000),你就会看到我的意思。

编辑:第一次错误地发布为评论。