2010-05-13 2 views
3

클래스를 디자인하려고하는데 중첩 된 필드에 액세스 할 때 문제가 발생하며 전체 디자인이 다중 스레드 안전성에 어떤 문제가 있습니다. 나는 이것이 어떻게 설계되어야하는지 또는 어떤 변화가 이루어져야하는지에 대해 더 잘 알고 있는지 알고 싶다.C# MultiThread 안전 클래스 디자인

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

이 할 수있는 어려운 일의 지옥이다.

많은 사람들이 시도하는 경향이있는 첫 번째 방법은 잠금을 추가하고 잠금을 사용하지 않고 변경 가능한 데이터를 다루는 코드가 없도록하는 것입니다. 즉, 클래스의 모든 내용이 변경 될 수 있음을 의미합니다. 즉, 데이터를 만지기 전에 먼저 잠금 객체를 잠글 수 있습니다. 읽거나 잠그기 만하면됩니다.

그러나 이것이 해결책이라면 코드에 아무 것도하지 말아야합니다. 클래스가 스레드로부터 안전하지 않다는 것을 문서화하고 그것을 사용하는 프로그래머에게 맡깁니다.

왜?

사실상 모든 액세스를 직렬화했기 때문에 가능합니다. 클래스의 다른 부분을 만지더라도 클래스를 동시에 사용하려고하는 두 개의 스레드는 차단됩니다. 스레드 중 하나가 액세스 권한이 부여되고 다른 스레드는 첫 번째 스레드가 완료 될 때까지 대기합니다.

이것은 실제로 클래스의 멀티 스레드 사용을 방해합니다. 따라서이 경우 클래스에 잠금을 설정하는 오버 헤드가 추가되고 실제 이점이 없습니다. 예, 클래스는 이제 "스레드 안전"이지만 실제로는 유용한 스레드 - 시민은 아닙니다.

다른 방법은 세분화 된 잠금을 추가하거나 잠금이 필요없는 구성을 작성하는 것입니다 (심각하게 어렵습니다). 따라서 객체의 두 부분이 항상 관련되지 않으면 각 부분에 액세스하는 코드가 자체 잠금을 갖습니다. 이렇게하면 데이터의 다른 부분을 액세스하는 여러 스레드가 서로 블로킹하지 않고 병렬로 실행될 수 있습니다.

올바른 순서로 잠금을 취하는 데주의를 기울여야하거나 교착 상태가 발생하기 때문에 한 번에 두 개 이상의 데이터 부분을 작업해야하는 경우 어디에서든 어려워집니다. 클래스를 사용하는 코드가 아니라 올바른 순서로 잠금을 설정하는 것은 클래스의 책임입니다.

구체적인 예를 들면, 백그라운드 스레드에서 변경 될 부분이 "장치 연결됨"부울 값만있는 것처럼 보입니다. 이 경우 나는 그 필드를 휘발성으로 만들 것이고 각각의 주위에 락을 사용할 것이다. 그러나 장치 목록이 백그라운드 스레드에서 변경되면 문제가 빨리 발생합니다.

먼저 백그라운드 스레드에 의해 변경 될 모든 부분을 확인한 다음 변경 내용을 다른 스레드로 전파하는 방법, 변경 내용에 대한 대응 방법 등을 고려해야합니다.