example - c# thread lock best practice




Esempio di codice per illustrare un deadlock utilizzando lock(this) (4)

Certo, ecco a te.

Si noti che l'esempio comune di un deadlock si ha quando si acquisiscono più blocchi e due o più thread finiscono per attendere l'un l'altro.

Ad esempio, due thread che si bloccano in questo modo:

Thread 1               Thread 2
 Lock "A"               Lock "B"
 Lock "B"               Lock "A" <-- both threads will stop dead here
                                     waiting for the lock to be come
                                     available.

Tuttavia, in questo esempio non me ne sono preoccupato, ho solo lasciato bloccare un thread per un tempo indefinito. Davvero non vuoi perdere il controllo sui tuoi lucchetti, quindi mentre questo è un esempio forzato, il fatto che il thread in background possa bloccare completamente il thread principale come questo, è sbagliato.

using System;
using System.Threading;

namespace ConsoleApplication7
{
    public class Program
    {
        public static void Main(string[] args)
        {
            LockableClass lockable = new LockableClass();
            new Thread(new ParameterizedThreadStart(BackgroundMethod)).Start(lockable);
            Thread.Sleep(500);
            Console.Out.WriteLine("calling Reset");
            lockable.Reset();
        }

        private static void BackgroundMethod(Object lockable)
        {
            lock (lockable)
            {
                Console.Out.WriteLine("background thread got lock now");
                Thread.Sleep(Timeout.Infinite);
            }
        }
    }

    public class LockableClass
    {
        public Int32 Value1 { get; set; }
        public Int32 Value2 { get; set; }

        public void Reset()
        {
            Console.Out.WriteLine("attempting to lock on object");
            lock (this)
            {
                Console.Out.WriteLine("main thread got lock now");
                Value1 = 0;
                Value2 = 0;
            }
        }
    }

}

Ho letto diversi articoli e post che dicono che lock(this) , lock(typeof(MyType)) , lock("a string") sono tutte cattive pratiche perché un altro thread potrebbe bloccare la stessa chiave e causare un deadlock. Per capire questo problema, stavo cercando di creare un codice di esempio per illustrare il deadlock ma non sono stato in grado di capirlo.

Qualcuno può scrivere un breve pezzo di codice che illustri questo classico problema? Per favore, tienilo breve, posso digerire il codice solo in blocchi più piccoli.

Edit: Penso che lassevk riassume bene; il vero problema è che hai perso il controllo sulle tue serrature. Quando ciò accade, non puoi controllare l'ordine in cui vengono chiamati i blocchi e stai consentendo una potenziale situazione di deadlock.

lock(this) , lock(typeof(MyType)) , ecc. sono tutte situazioni in cui è stato scelto un lock che è impossibile controllare.


Il problema è che il blocco ("una stringa") si blocca su un singleton. Ciò significa che altri oggetti che utilizzano lo stesso blocco potrebbero essere un'attesa infinita.

per esempio:

using System;
using System.Threading;

namespace ThreadLock
{
    class Program
    {
        static void Main(string[] args)
        {
            lock ("my lock")
            {
                ManualResetEvent evt = new ManualResetEvent(false);
                WorkerObject worker = new WorkerObject(evt);
                Thread t = new Thread(new ThreadStart(worker.Work));
                t.Start();
                evt.WaitOne();
            }
        }
    }

    class WorkerObject
    {
        private ManualResetEvent _evt;
        public WorkerObject(ManualResetEvent evt)
        {
            _evt = evt;
        }
        public void Work()
        {
            lock ("my lock")
            {
                Console.WriteLine("worked.");
                _evt.Set();
            }
        }
    }
}

In questo caso, il codice chiamante crea un blocco su una stringa e quindi crea un oggetto worker. L'oggetto worker in Work () si blocca sulla stessa stringa, che è un singleton in C #. Finisce in un deadlock perché il chiamante possiede il blocco e sta aspettando un segnale che non arriverà mai.


Questa è una pessima cattiveria. Grabing le serrature fuori ordine e poi a dormire con la serratura. Due cose brutte da fare. :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace DeadLock
{
    public class Program
    {
        static void Main(string[] args)
        {
            var ddt = new DontDoThat();

            ddt.Go();
        }
    }

    public class DontDoThat
    {
        private int _badSharedState = 0;
        private readonly object _lock1 = new object();
        private readonly object _lock2 = new object();

        public void Go()
        {
            new Thread(BadGuy1).Start();
            new Thread(BadGuy2).Start();

            Console.WriteLine("Leaving Go!");
        }

        public void BadGuy1()
        {
            lock (_lock1)
            {
                Thread.Sleep(100); // yeild with the lock is bad
                lock (_lock2)
                {
                    _badSharedState++;
                    Console.Write("From Bad Guy #1: {0})", _badSharedState );
                }
            }
        }
        public void BadGuy2()
        {
            lock (_lock2)
            {
                lock (_lock1)
                {
                    _badSharedState++;
                    Console.Write("From Bad Guy #2: {0})", _badSharedState);
                }
            }
        }
    }
}

Un deadlock si verifica solo se si dispone di più di un blocco. Hai bisogno di una situazione in cui entrambi i thread contengono una risorsa di cui l'altra ha bisogno (il che significa che ci devono essere almeno due risorse e che i due thread devono tentare di acquisirli in un ordine diverso)

Quindi un semplice esempio:

// thread 1
lock(typeof(int)) {
  Thread.Sleep(1000);
  lock(typeof(float)) {
    Console.WriteLine("Thread 1 got both locks");
  }

}

// thread 2
lock(typeof(float)) {
  Thread.Sleep(1000);
  lock(typeof(int)) {
    Console.WriteLine("Thread 2 got both locks");
  }
}

Supponendo che entrambi i thread siano iniziati entro un secondo l'uno dall'altro, entrambi avranno il tempo di afferrare il primo lucchetto prima che qualcuno arrivi al lucchetto interno. Senza la chiamata Sleep (), uno dei thread probabilmente avrà il tempo di ottenere e rilasciare entrambi i blocchi prima ancora che inizi l'altro thread.





deadlock