Multithreading in C#, Teil 13: Synchronization Context

Im Teil 11 dieser Artikelserie habe ich Ihnen gezeigt wie ein manuelles Sperren von Code Abschnitten mittels Monitor, lock, Mutex und Semaphoren erfolgen kann. Im heutigen Artikel möchte ich Ihnen eine alternative Möglichkeit zu diesen manuellen Mechanismen vorstellen. Diese Alternative besteht in einem deklarativen Sperrmechanismus mittels eines Synchronization Context. Als Synchronization Context wird dabei der gesperrte Bereich bezeichnet.

 
ContextBoundObject

Ein deklarativer Sperrmechanismus wird durch eine Ableitung von ContextBoundObject realisiert. Zusätzlich muss das Synchronization Attribut angegeben werden. Der nachfolgende Quellcode zeigt ein entsprechendes Beispiel.

static void Main(string[] args)
{
    LockedClass test = new LockedClass();

    for (int i = 1; i <= 10; i++)
    {
        new Thread(test.DoSomething).Start();
    }

    Console.ReadKey();
}

[Synchronization]
class LockedClass : ContextBoundObject
{
    public void DoSomething()
    {
        Console.Write("Do something...");
        Thread.Sleep(100);
        Console.WriteLine("finished");
    }
}

 
Die Konsolenanwendung erzeugt folgende Ausgabe:

Do something…finished

Do something…finished

Do something…finished

 
Wie sie sehen können, erfolgt der Zugriff threadsicher. Die Funktion kann jeweils nur von einem Thread ausgeführt werden. Der deklarativ erzeugte Sperrmechanismus sorgt also dafür, dass die komplette Klasse threadsicher gestaltet wird.

 
Mittels dieser deklarativen Sperren werden auch die Zugriffe auf mehrere unterschiedliche Funktionen abgesichert.

static void Main(string[] args)
{
    LockedClass test = new LockedClass();

    for (int i = 1; i <= 10; i++)
    {
        if (i % 2 == 1)
        {
            new Thread(test.MyFirstFunction).Start();
        }
        else
        {
            new Thread(test.MySecondFunction).Start();
        }
    }

    Console.ReadKey();
}

[Synchronization]
class LockedClass : ContextBoundObject
{
    public void MyFirstFunction()
    {
        Console.Write("Execute MyFirstFunction...");
        Thread.Sleep(100);
        Console.WriteLine("finished");
    }

    public void MySecondFunction()
    {
        Console.Write("Execute MySecondFunction...");
        Thread.Sleep(100);
        Console.WriteLine("finished");
    }
}

 
Die Konsolenanwendung erzeugt folgende Ausgabe:

Execute MyFirstFunction……finished

Execute MySecondFunction…finished

Execute MyFirstFunction……finished

Execute MySecondFunction…finished

 
Das heisst, auch der Zugriff auf unterschiedliche Funktionen ist threadsicher umgesetzt und erfolgt daher sequentiell.

 
Verschachtelte Klassen mit Synchronization Context

Wird innerhalb einer Klasse welche von ContextBoundObject abgeleitet ist eine andere Klasse instanziiert, welche ebenfalls von ContextBoundObject abgeleitet ist, dann wird standardmässig ein gemeinsamer Synchronization Context verwendet. Der nachfolgende Quellcode zeigt ein entsprechendes Beispiel.

static void Main(string[] args)
{
    LockedMasterClass test = new LockedMasterClass();

    for (int i = 1; i <= 10; i++)
    {
        new Thread(test.DoSomething).Start();
    }

    Console.ReadKey();
}

[Synchronization]
class LockedClass : ContextBoundObject
{
    public void DoSomething()
    {
        Console.Write("Do something...");
        Thread.Sleep(100);
        Console.WriteLine("finished");
    }
}

[Synchronization]
class LockedMasterClass : ContextBoundObject
{
    LockedClass lockedClass = new LockedClass();

    public void DoSomething()
    {
        lockedClass.DoSomething();
    }
}

 
Die Klasse LockedMasterClass erzeugt intern eine Instanz der Klasse LockedClass. Da beide Klassen von ContextBoundObject abgeleitet sind wird ein gemeinsamer Speerbereich über beide Klassen hinweg erzeugt.

Nachteil dieser Lösung ist, dass ein grosser gemeinsamer Sperrbereich erzeugt wird. Je grösser der gesperrte Bereich ist umso weniger Funktionalität kann parallel ausgeführt werden. Dadurch wird die Performance der Anwendung verschlechtert.

Das Synchronization Attribut bietet daher die Möglichkeit dieses Verhalten zu ändern. Die folgenden Konstanten können beim Anlegen des Synchronization Attributs angegeben werden.

NOT_SUPPORTED

 

Das Synchronization Attribute wird nicht verwendet.
SUPPORTED

 

Wenn die Instanz in einer anderen Synchronization   Objects erstellt wird, dann wird ein gemeinsamer Synchronisation Contect   verwendet. Andernfalls erfolgt keine Synchronisation.
REQUIRED

 

Dies ist der Default Wert.

Wenn die Instanz in einer anderen Synchronization   Objects erstellt wird, dann wird ein gemeinsamer Synchronisation Contect   verwendet. Andernfalls wird ein neuer Synchronisation Context erzeugt.

REQUIRES_NEW

 

Es wird immer ein neuer Synchronization Context   erzeugt, unabhängig davon ob die Instanz direkt erzeugt wird oder innerhalb   eines anderen Synchronization Objects.

 

Der nachfolgende Quellcode zeigt die Verwendung dieses Attributs.

[Synchronization(SynchronizationAttribute.REQUIRES_NEW)]
class LockedClass : ContextBoundObject
{
    public void DoSomething()
    {
        Console.Write("Do something...");
        Thread.Sleep(100);
        Console.WriteLine("finished");
    }
}

[Synchronization]
class LockedMasterClass : ContextBoundObject
{
    LockedClass lockedClass = new LockedClass();

    public void DoSomething()
    {
        lockedClass.DoSomething();
    }
}

 
Fazit

Der deklarative Sperrmechanismus mittels Ableitung von ContextBoundObject bietet eine einfache und schnelle Möglichkeit threadsichere Klassen zu schreiben. Zu beachten ist dabei, dass von ContextBoundObject abgeleitet werden muss und daher von keiner weiteren Klasse abgeleitet werden kann. Ein weiterer Nachteil besteht darin, das ein relativ grosser Sperrbereich um das Objekt erzeugt wird. Dies macht eine differenzierte parallele Ausführung von unabhängigen Funktionen der Klasse unmöglich. In Situationen in denen diese beiden Punkte keine Rolle spielen, ermöglicht der deklarative Sperrmechanismus eine effiziente threadsicher Implementierung.

Advertisements
Dieser Beitrag wurde unter .NET, C#, Multithreading abgelegt und mit , , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s