4 Stimmen

C# MultiThread Safe Class Entwurf

Ich versuche, eine Klasse zu entwerfen, und ich habe Probleme mit dem Zugriff auf einige der verschachtelten Felder und ich habe einige Bedenken, wie Multithread-sicher das gesamte Design ist. Ich würde gerne wissen, ob jemand eine bessere Vorstellung davon hat, wie dies entworfen werden sollte oder ob irgendwelche Änderungen vorgenommen werden sollten?

using System;
using System.Collections;

namespace SystemClass
{
public class Program
{
    static void Main(string[] args)
    {
        System system = new System();

        //Seems like an awkward way to access all the members
        dynamic deviceInstance = (((DeviceType)((DeviceGroup)system.deviceGroups[0]).deviceTypes[0]).deviceInstances[0]);
        Boolean checkLocked = deviceInstance.locked;

        //Seems like this method for accessing fields might have problems with multithreading
        foreach (DeviceGroup dg in system.deviceGroups)
        {
            foreach (DeviceType dt in dg.deviceTypes)
            {
                foreach (dynamic di in dt.deviceInstances)
                {
                    checkLocked = di.locked;
                }
            }
        }
    }
}

public class System
{
    public ArrayList deviceGroups = new ArrayList();

    public System()
    {   
        //API called to get names of all the DeviceGroups
        deviceGroups.Add(new DeviceGroup("Motherboard"));
    }
}

public class DeviceGroup
{
    public ArrayList deviceTypes = new ArrayList();

    public DeviceGroup() {}

    public DeviceGroup(string deviceGroupName)
    {
        //API called to get names of all the Devicetypes
        deviceTypes.Add(new DeviceType("Keyboard"));
        deviceTypes.Add(new DeviceType("Mouse"));
    }
}

public class DeviceType
{
    public ArrayList deviceInstances = new ArrayList();
    public bool deviceConnected;

    public DeviceType() {}

    public DeviceType(string DeviceType)
    {
        //API called to get hardwareIDs of all the device instances
        deviceInstances.Add(new Mouse("0001"));
        deviceInstances.Add(new Keyboard("0003"));
        deviceInstances.Add(new Keyboard("0004"));

        //Start thread CheckConnection that updates deviceConnected periodically
    }

    public void CheckConnection()
    {
        //API call to check connection and returns true
        this.deviceConnected = true;
    }
}

public class Keyboard
{
    public string hardwareAddress;
    public bool keypress;
    public bool deviceConnected;

    public Keyboard() {}

    public Keyboard(string hardwareAddress)
    {
        this.hardwareAddress = hardwareAddress;
        //Start thread to update deviceConnected periodically
    }

    public void CheckKeyPress()
    {
        //if API returns true
        this.keypress = true;
    }
}

public class Mouse
{
    public string hardwareAddress;
    public bool click;

    public Mouse() {}

    public Mouse(string hardwareAddress)
    {
        this.hardwareAddress = hardwareAddress;
    }

    public void CheckClick()
    {
        //if API returns true
        this.click = true;
    }
}

}

3voto

Lasse V. Karlsen Punkte 364542

Eine Klasse thread-sicher zu machen, ist eine verdammt schwierige Sache.

Der erste, naive Weg, den viele versuchen, besteht darin, einfach eine Sperre hinzuzufügen und sicherzustellen, dass kein Code, der veränderbare Daten berührt, dies tut, ohne die Sperre zu verwenden. Damit meine ich, dass alles in der Klasse, das einer Änderung unterliegt, zuerst das Sperrobjekt sperren muss, bevor es die Daten berührt, sei es nur zum Lesen oder Schreiben von Daten.

Wenn dies jedoch Ihre Lösung ist, dann sollten Sie wahrscheinlich gar nichts am Code ändern, sondern nur dokumentieren, dass die Klasse nicht thread-sicher ist, und dies dem Programmierer überlassen, der sie verwendet.

Warum?

Denn Sie haben den Zugriff darauf praktisch in Serie geschaltet. Zwei Threads, die versuchen, die Klasse gleichzeitig zu verwenden, obwohl sie unterschiedliche Teile davon berühren, werden blockieren. Einem der Threads wird der Zugriff gewährt, der andere wartet, bis der erste Thread fertig ist.

Dies ist eigentlich entmutigen Multi-Thread-Nutzung Ihrer Klasse, so dass in diesem Fall Sie Overhead von Sperren zu Ihrer Klasse hinzufügen, und nicht tatsächlich erhalten keine Vorteile davon. Ja, Ihre Klasse ist jetzt "thread-sicher", aber sie ist nicht wirklich ein guter Thread-Bürger.

Die andere Möglichkeit besteht darin, granulare Sperren hinzuzufügen oder sperrfreie Konstrukte zu schreiben (was wirklich schwierig ist), so dass Code, der auf die einzelnen Teile eines Objekts zugreift, seine eigene Sperre hat, wenn diese nicht immer miteinander verbunden sind. Dies würde es mehreren Threads, die auf verschiedene Teile der Daten zugreifen, ermöglichen, parallel zu laufen, ohne sich gegenseitig zu blockieren.

Dies wird immer dann schwierig, wenn man an mehr als einem Teil der Daten gleichzeitig arbeiten muss, da man sehr sorgfältig darauf achten muss, die Sperren in der richtigen Reihenfolge zu nehmen, sonst kommt es zu Deadlocks. Es sollte die Aufgabe Ihrer Klasse sein, dafür zu sorgen, dass die Sperren in der richtigen Reihenfolge genommen werden, nicht der Code, der die Klasse verwendet.

Was Ihr spezifisches Beispiel angeht, sieht es für mich so aus, als ob die Teile, die sich von Hintergrund-Threads ändern, nur die booleschen Werte für "Ist das Gerät angeschlossen" sind. In diesem Fall würde ich dieses Feld flüchtig machen und eine Sperre um jedes Feld herum verwenden. Wenn jedoch die Liste der Geräte wird von Hintergrund-Threads ändern, werden Sie in Probleme ziemlich schnell laufen.

Sie sollten zunächst versuchen, alle Teile zu identifizieren, die von Hintergrund-Threads geändert werden, und dann Szenarien dafür entwerfen, wie die Änderungen auf andere Threads übertragen werden sollen, wie auf die Änderungen reagiert werden soll usw.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X