C#中锁定不起作用

3

Lock锁定机制的效果不如预期,以下是代码。 我在这里应用了线程,但我将在ASP.NET应用程序中应用它。

class Program
    {
        static void Main(string[] args)
        {
            ThreadManager.CurrentSession = 0;
            for (int i = 0; i < 10; i++)
            {
                CreateWork objCreateWork = new CreateWork();
                ThreadStart start = new ThreadStart(objCreateWork.ProcessQuickPLan);
                new Thread(start).Start();
            }
            Console.ReadLine();
        }
    }

    class CreateWork
    {
        private object CurrentSession = -1;
        public void ProcessQuickPLan()
        {
            lock (CurrentSession)
            {
                CurrentSession = ThreadManager.CurrentSession;
                Console.WriteLine(CurrentSession);
                ThreadManager.CurrentSession = Convert.ToInt32(ThreadManager.CurrentSession) + 1;
            }
        }
    }

    class ThreadManager
    {
        public static object CurrentSession
        {
            get;
            set;
        }
    }

它正在给我以下输出:
0
0
0
3
4
4
6
7
8
9

And I am expecting

0
1
2
3
4
5
6
7
8
9

我做错了什么?

我是否应该像这里描述的那样使用readonly object C# lock(mylocker) not work


我无法使用“readonly”对象,请帮忙寻找替代方案如果“readonly”不是解决方案。 - vikas
2
为什么不能使用readonly?这种人为的限制总是很奇怪。 - H H
5个回答

4
问题在于您所使用的锁定对象。您正在使用实例变量,因此每个实例都有自己的锁定变量,这是基本错误的。
第二个问题是使用“-1”进行初始化,这至少令人困惑。
简单的解决方法是使用静态对象: static object CurrentSession = new object(); 下一个问题是CurrentSession = ThreadManager.CurrentSession;,这没有任何意义,本质上是错误的。我很惊讶它甚至能编译。
class CreateWork
{
    private object CurrentSession = -1;   // boxed int, Id only
    private static object _locker = new object();

    public void ProcessQuickPLan()
    {
        lock (_locker)
        {
            CurrentSession = ThreadManager.CurrentSession;
            Console.WriteLine(CurrentSession);
            ThreadManager.CurrentSession = Convert.ToInt32(ThreadManager.CurrentSession) + 1;
        }
    }
}

简介:不清楚您在这里想要做什么。CurrentSession似乎具有锁定守卫和Id的双重角色,这并不是一个好计划。

基本上,您希望有1个私有静态对象来保护资源。初始化后不要再分配给它。


3
问题在于每个线程都有自己的锁。将CurrentSession设为静态应该可以解决这个问题:只会有一个对象用于锁定。您还应该停止在代码中重新分配它。
class CreateWork
{
    private static readonly object LockObject = -1; // Although -1 works here, it's really misleading
    // You should consider replacing the above with a "plain" new object();
    private object CurrentSession = -1; 
    public void ProcessQuickPLan()
    {
        lock (LockObject)
        {
            CurrentSession = ThreadManager.CurrentSession;
            Console.WriteLine(CurrentSession);
            ThreadManager.CurrentSession = Convert.ToInt32(ThreadManager.CurrentSession) + 1;
        }
    }
}

这里有一个在ideone上的工作演示

CurrentSession 是一个静态对象。 - vikas
1
@WouterdeKort 那个问题与 OP 所做的事情完全无关:他使用装箱的 Int32 并不意味着 object 不适合用于锁定。 - Sergey Kalinichenko
@dasblinkenlight private static object LockObject = -1; 这行代码非常有帮助,我离解决问题已经很接近了。感谢您的回答。您能否详细说明一下锁是如何工作的,或者分享一些相关文章? - vikas
你:_你还应该停止重新分配它_正确。那么,为了更加确保不被重新分配,为什么不在字段上使用readonly修饰符呢? - Jeppe Stig Nielsen
@JeppeStigNielsen 非常好的建议,我进行了编辑。谢谢! - Sergey Kalinichenko
显示剩余6条评论

0

每个线程都包含一个带锁的CreateWork实例。尝试使用以下代码:

class Program
{
    static void Main(string[] args)
    {
        ThreadManager.CurrentSession = 0;
        CreateWork objCreateWork = new CreateWork();
        for (int i = 0; i < 10; i++)
        {
            ThreadStart start = new ThreadStart(objCreateWork.ProcessQuickPLan);
            new Thread(start).Start();
        }
        Console.ReadLine();
    }
}

class CreateWork
{
    private object CurrentSession = -1;
    public void ProcessQuickPLan()
    {
        lock (CurrentSession)
        {
            CurrentSession = ThreadManager.CurrentSession;
            Console.WriteLine(CurrentSession);
            ThreadManager.CurrentSession = Convert.ToInt32(ThreadManager.CurrentSession) + 1;
        }
    }
}

class ThreadManager
{
    public static object CurrentSession
    {
        get;
        set;
    }
}

0

将您的代码更改为以下内容:

using System;
using System.Threading;
class Program
{
    static void Main(string[] args)
    {
        ThreadManager.CurrentSession = 0;
        for (int i = 0; i < 10; i++)
        {
            CreateWork objCreateWork = new CreateWork();
            ThreadStart start = new ThreadStart(objCreateWork.ProcessQuickPLan);
            new Thread(start).Start();
        }
        Console.ReadLine();
    }
}

class CreateWork
{
    private static object _lock = new Object();

    public void ProcessQuickPLan()
    {
        lock (_lock)
        {            
            Console.WriteLine(ThreadManager.CurrentSession);
            ThreadManager.CurrentSession++;
        }
    }
}

class ThreadManager
{
    public static int CurrentSession
    {
        get;
        set;
    }
}

重要的是将锁和跟踪ID分开。

私有锁是一个静态对象,因此它在线程之间共享。我还删除了每次分配新值给锁的操作。


0
我认为问题在于,你在自己的线程中锁定了一个对象,所以它实际上永远不会被锁定。
最好使用全局对象进行锁定。

我实际上没有明白,请详细说明。 - vikas
@vikas,你有一个private object CurrentSession。这里没有static。这意味着每个新的CreateWork实例都有自己的CurrentSession对象。因此,它们锁定的是不同的对象。为了使其正常工作,必须锁定相同的实例。将该字段设置为static(或“全局”)。 - Jeppe Stig Nielsen
@JeppeStigNielsen,它的意思是对象应该是静态的。 - vikas

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