4

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

そして、私は期待しています

0
1
2
3
4
5
6
7
8
9

私はどこで間違っていますか?

readonly objectここで説明されているよう に使用する必要がありますC# lock(mylocker) が機能しません

4

5 に答える 5

3

問題は、ロックオンに使用するオブジェクトにあります。インスタンス変数を使用しているため、各インスタンスには独自の がありますがlock、これは根本的に間違っています。

2 番目の問題は、での初期化-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 つのプライベートな静的オブジェクトが必要です。初期化後は割り当てないでください。

于 2013-08-09T10:31:44.130 に答える
2

問題は、各スレッドに独自のロックがあることです。staticCurrentSessionにすると問題が解決するはずです。ロックするオブジェクトが 1 つだけになります。また、コードでの再割り当てを停止する必要があります。

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 で動作するデモです

于 2013-08-09T10:33:44.480 に答える
0

問題は、オブジェクトを独自のスレッドでロックするため、実際にはロックされないことだと思います。

ロックされるグローバル オブジェクトを使用することをお勧めします。

于 2013-08-09T10:32:53.223 に答える
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 の追跡を分離することです。

プライベート ロックは静的オブジェクトであるため、スレッド間で共有されます。また、毎回新しい値をロックに割り当てることも削除しました。

于 2013-08-09T10:48:02.150 に答える