2010-05-13 6 views
3

J'essaie de concevoir une classe et j'ai des problèmes avec l'accès à certains des champs imbriqués et j'ai quelques soucis avec la sécurité multithread tout le design est. Je voudrais savoir si quelqu'un a une meilleure idée de la façon dont cela devrait être conçu ou si des changements devraient être apportés?C# MultiThread Safe Class Design

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; 
    } 
} 

}

Répondre

2

Faire est un diable d'une chose difficile à faire une classe thread-safe.

La première façon, naïve, que beaucoup ont tendance à essayer consiste simplement à ajouter un verrou et à s'assurer qu'aucun code qui touche les données mutables le fait sans utiliser le verrou. J'entends par là que tout ce qui est sujet à changement dans la classe doit d'abord verrouiller l'objet de verrouillage avant de toucher les données, qu'il s'agisse simplement de lire ou d'écrire dessus.

Cependant, si c'est votre solution, alors vous ne devriez probablement rien faire du tout, juste documenter que la classe n'est pas thread-safe et la laisser au programmeur qui l'utilise.

Pourquoi?

Parce que vous avez effectivement juste sérialisé tous les accès. Deux threads essayant d'utiliser la classe en même temps, même s'ils en touchent des parties séparées, bloqueront. L'un des threads aura accès, l'autre attendra que le premier soit terminé. Cela décourage en fait l'utilisation multithread de votre classe, dans ce cas, vous ajoutez un surcoût de verrouillage à votre classe, sans en tirer aucun bénéfice. Oui, votre classe est maintenant "thread safe", mais ce n'est pas vraiment un bon thread-citoyen. L'autre façon est de commencer à ajouter des verrous granulaires, ou écrire des constructions sans verrou (sérieusement dur), de sorte que si deux parties de l'objet ne sont pas toujours liées, le code qui accède à chaque partie a son propre verrou. Cela permettrait à plusieurs threads accédant à différentes parties des données de fonctionner en parallèle sans se bloquer les uns les autres.

Cela devient difficile partout où vous devez travailler sur plus d'une partie des données à la fois, car vous devez être très prudent pour prendre les verrous dans le bon ordre, ou subir des blocages. Ce devrait être la responsabilité de votre classe de s'assurer que les verrous sont pris dans le bon ordre, pas le code qui utilise la classe. En ce qui concerne votre exemple spécifique, il me semble que les parties qui changeront à partir des threads d'arrière-plan ne sont que les valeurs booléennes "est le périphérique connecté". Dans ce cas, je rendrais ce champ volatile et utiliserais un verrou autour de chacun. Si, toutefois, la liste des périphériques change à partir des threads de fond, vous allez rencontrer des problèmes assez rapidement.

Vous devriez d'abord essayer d'identifier toutes les parties qui seront modifiées par des fils de fond, puis concevoir des scénarios pour la façon dont vous voulez que les modifications propagées à d'autres fils, comment réagir aux changements, etc.