1

シングルトーン オブジェクトへのアクセスを制限しようとしているので、一度に 1 つのスレッドだけがそれを使用します。さらに、同じスレッドが制限されたコードに 2 回アクセスするのを防ぎたいです。

Lock メソッドを試してみたところ、彼女をロックしたスレッドはロックされず、他のスレッドのみがロックされることがわかりました..

以下のように:

public sealed class Singleton
{
    private static readonly Singleton instance    = new Singleton();

    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    { 
        get
        {
            return instance;
        }
    }
}

public class SomeWorker
{
    private readonly Timer _doWorkTimer = new Timer(20);

    public SomeWorker()
    {
        InitiateTimer();
    }

    private void InitiateTimer()
    { 
        _doWorkTimer .Elapsed += DoWorkElapse;
        _doWorkTimer .Enabled = true;
    }

    private void DoWorkElapse(object source, ElapsedEventArgs e)
    { 
        DoSomeWork();
    }

    private void DoSomeWork()
    {
        // I know that lock on string is wrong!
        // Its just for the example only I
        // Its just to make sure all the program is use the same lock..
        lock ("ConnectionLock")
        { 
             Console.WriteLine("Lock");

             var inst = Singletone.Instance;

             // Do Some Work on "inst" ...

             Console.WriteLine("Unlock");
        }
    }
}

たとえば、コンソールの結果は次のようになります。

. . .

ロック

ロック解除

ロック

ロック

ロック解除

. . .

ご覧のとおり、2 つの Lock コメントが次々と表示されます

つまり、「DoSomeWork()」がタイマー スレッドによって 2 回アクセスされることを意味します。

このロックを機能させる方法を知っている人はいますか?

他の同期方法はありますか?

ありがとう。

4

3 に答える 3

4

あなたはロックを適切に行っていません(さらに、文字列をロックしていることは大きな問題です)。時間を節約するには、Jon Skeet によるこの記事を読み、パターンの 1 つを実装して頭を悩ませないようにしてください。

于 2012-12-05T00:30:57.383 に答える
1

あなたのコードでは

public static Singletone Instance()
{
    if (_instance == null)
    {
        lock (_instance)
        {
            if (_instance == null)
            {
                _instance = new Singletone ();
            }
        }
    }
    return _instance;;
}

考えてみてください。if (_instance == null)あなたがしますlock (_instance)。したがって、を使用してロックしnullます。それはまったく良くありません。

MSDNロック ステートメント (C# リファレンス)では、使用方法の例を次に示しますlock

class Account
{
    decimal balance;
    private Object thisLock = new Object();

    public void Withdraw(decimal amount)
    {
        lock (thisLock)
        {
            if (amount > balance)
            {
                throw new Exception("Insufficient funds");
            }
            balance -= amount;
        }
    }
}

それに従って、別のオブジェクトをロックとして使用する必要があると思います。


次に、スレッド同期プリミティブを使用して、異なるスレッドの共有リソースへのアクセスを分離します。アクセスを 1 つのスレッドから分離する必要がある場合は、フラグを使用するだけです。このようなもの:

bool isBusy = false;
public static void Foo()
{
    if (!isBusy)
    {
        isBusy = true;
        try
        {            
            //do the job
        }
        finally
        {
            isBusy = false;
        }
    }
}

ここでは、単に "locked-by-flag" コードをスキップしていることを理解する必要があります。それどころか、特にマルチスレッド アプリケーションで、スレッド自体を待機させたい場合は、再設計する必要があるように思われます。

于 2012-12-05T00:41:45.257 に答える
0

.NET でシングルトンを実装する最も簡単な方法は次のとおりです。

public class Singleton : IDisposable
{
    private readonly static Singleton _instance = new Singleton();
    private readonly static object lockObject = new object();

    static Singleton()
    {
    }

    private Singleton()
    {
        InitiateConnection();
    }

    public static Singleton Instance
    {
        get { return _instance; }
    }

    /// <summary>
    /// Method that accesses the DB.
    /// </summary>
    public void DoWork()
    {
        lock (lockObject)
        {
            //Do Db work here. Only one thread can execute these commands at a time.                
        }
    }        

    ~Singleton()
    {
        //Close the connection to DB.

        //You don't want to make your singleton class implement IDisposable because
        //you don't want to allow a call to Singleton.Instance.Dispose().
    }
}

Bryan が回答で提案した.NET でのシングルトン パターンの実装に関する優れた記事を読んでください。上記の実装は、記事で説明されている 4 番目のバージョンに基づいています。CLR は、静的フィールドの構築がスレッドセーフであることを保証するため、そこでロックする必要はありません。ただし、オブジェクトに変更可能な状態 (フィールド) がある場合は、ロックが必要になります。

private readonly objectメソッドには相互排除を保証するために使用されることに注意してくださいDoWork。このようにして、一度に 1 つのスレッドを呼び出すことができますDoWork。また、スレッドは命令を順番に実行するため、同じスレッドがこのメソッドを同時に 2 回呼び出すことはできないことに注意してください。このメソッドを 1 つのスレッドから 2 回呼び出すことができる唯一の方法はDoWork、最終的に を呼び出す別のメソッドを内部で呼び出す場合ですDoWork。これを行う意味がわかりません。そうする場合は、スタックオーバーフローを避けるように注意してください。Konstantinの提案に従ってフラグを使用することもできますが、私見では、DoWork1つのことだけを行い、このようなシナリオを回避するように再設計する必要があります.

于 2012-12-05T13:45:41.667 に答える