2010-05-13 3 views
3

Я пытаюсь создать класс, и у меня возникли проблемы с доступом к некоторым вложенным полям, и у меня есть некоторые проблемы с тем, как многопоточный сейф в целом. Я хотел бы знать, есть ли у кого-нибудь лучшее представление о том, как это должно быть разработано или какие-либо изменения должны быть сделаны?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; 
    } 
} 

}

ответ

2

Создание класса потокобезопасной является чертовски трудным делом.

Первый, наивный, способ, к которому многие стремятся, - это просто добавить блокировку и гарантировать, что ни один код, затрагивающий изменчивые данные, не делает этого без использования блокировки. Под этим я подразумеваю, что все в классе, который может быть изменен, должен сначала заблокировать объект блокировки, прежде чем касаться данных, будь то просто чтение из него или запись на него.

Однако, если это ваше решение, вы, вероятно, не должны вообще что-то делать с кодом, просто укажите, что класс не является потокобезопасным и оставьте его программисту, который его использует.

Почему?

Поскольку вы фактически просто сериализовали весь доступ к нему. Два потока, которые пытаются использовать класс одновременно, даже если они касаются отдельных его частей, будут блокироваться. Одному из потоков будет предоставлен доступ, другой будет ждать завершения первого.

Это фактически обескураживает многопоточное использование вашего класса, поэтому в этом случае вы добавляете накладные расходы на блокировку для своего класса и не получаете от этого никаких преимуществ. Да, ваш класс теперь «потокобезопасен», но на самом деле это не хороший поток-гражданин.

Другим способом является добавление зернистых замков или создание конструкций без блокировки (серьезно тяжело), ​​так что если две части объекта не всегда связаны, код, который обращается к каждой части, имеет свою собственную блокировку. Это позволит нескольким потокам обращаться к различным частям данных для параллельной работы без блокировки друг друга.

Это становится трудным, когда вам нужно работать с более чем одной частью данных за раз, так как вам нужно быть очень осторожным, чтобы взять замки в правильном порядке или страдать от тупиков. Должна быть ответственность вашего класса за то, чтобы блокировки выполнялись в правильном порядке, а не в коде, который использует класс.

Что касается вашего конкретного примера, то мне кажется, что части, которые будут меняться от фоновых потоков, являются только логическими значениями «связано устройство». В этом случае я бы сделал это поле изменчивым и использовал блокировку вокруг каждого. Если, однако, список устройств изменится из фоновых потоков, вы столкнетесь с проблемами довольно быстро.

Вы должны сначала попытаться определить все детали, которые будут изменены фоновыми потоками, а затем разработать сценарии, как вы хотите, чтобы изменения распространяются на другие потоки, как реагировать на изменения и т.д.