C#多线程安全类设计

标签 c# design-patterns c#-4.0 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;
    }
}

最佳答案

使类线程安全是一件非常困难的事情。

许多人倾向于尝试的第一种天真的方法只是添加一个锁,并确保在不使用锁的情况下,任何接触可变数据的代码都不会这样做。我的意思是,类中所有可能发生变化的东西,都必须在接触数据之前首先锁定锁定对象,无论是从数据中读取还是写入数据。

但是,如果这是您的解决方案,那么您可能根本不应该对代码做任何事情,只需记录该类不是线程安全的,并将其留给使用它的程序员。

为什么?

因为您实际上已经序列化了对它的所有访问。尝试同时使用该类的两个线程,即使它们正在接触它的不同部分,也会阻塞。其中一个线程将获得访问权限,另一个线程将等待第一个线程完成。

这实际上阻碍了类的多线程使用,因此在这种情况下,您在类中增加了锁定的开销,实际上并没有从中获得任何好处。是的,您的类现在是“线程安全的”,但它实际上并不是一个好的线程公民。

另一种方法是开始添加粒度锁,或编写无锁结构(非常困难),这样如果对象的两个部分并不总是相关,访问每个部分的代码都有自己的锁。这将允许访问数据不同部分的多个线程并行运行,而不会相互阻塞。

无论何时需要同时处理多个数据部分,这都会变得很困难,因为您需要格外小心,以正确的顺序获取锁,否则就会出现死锁。确保以正确的顺序获取锁应该是您的类的责任,而不是使用该类的代码。

至于您的具体示例,在我看来,从后台线程更改的部分似乎只是“设备是否已连接” bool 值。在这种情况下,我将使该字段可变,并在每个字段周围使用一个锁。但是,如果设备列表将从后台线程更改,您将很快遇到问题。

您应该首先尝试识别将由后台线程更改的所有部分,然后针对您希望更改如何传播到其他线程、如何对更改使用react等设计场景。

关于C#多线程安全类设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2824770/

相关文章:

qt - 鼠标交互的设计模式

java - 对象层次结构的工厂(多态静态方法解决方法)

asp.net-mvc - 将两个两个数据库上下文合并为一个?

c# - 使用 Moq 从另一个方法调用模拟方法

php - 观察者可以观察多个观察者吗?

c# - 添加方法导致编译错误 C# ASP

Silverlight 4 的 ListView 控件?

c#读取xml文件

c# - ASP.NET Core 和 CreateErrorResponse

c# - 在 datagridview 中显示按当前日期过滤的数据 C#