在 dotnet core 后台服务中处理实时系统共享状态的最佳方式

问题描述 投票:0回答:1

我在 dotnet core 3.1 中有一个后台服务

IHostedService
,它使用套接字(家庭滚动)接收来自数百个客户端(工厂中的机器)的请求。我的问题是,可以在不同线程上对有权访问对象(共享状态)的类上的同一方法进行多次调用。这在代码库中很常见。请求还必须按正确的顺序处理。

之所以不在数据库中是由于性能原因(实时系统)。我知道我可以使用锁,但我不想在整个代码库中都使用锁。

处理这种情况的标准方法是什么?您使用内存数据库吗?内存缓存?还是我只需要在各处加锁?

public class Machine
{
    
    public MachineState {get; set;}

    // Gets called by multiple threads from multiple clients 
    public bool CheckMachineStatus()
    {
        return MachineState.IsRunning;
    }

    // Gets called by multiple threads from multiple clients 
    public void SetMachineStatus()
    {
        MachineState = Stopped;
    }
}

更新

这是一个例子。我有一个控制台应用程序,可以通过套接字与机器通信,用于称重产品。当控制台应用程序初始化时,它会将数据加载到内存中(有关正在称重的产品的信息)。所有这些都是在主线程上完成的,以保持数据完整性。

当线程 1 上的称重器收到调用时,它将切换到主线程以访问产品信息,并完成任何其他工作,例如为系统其他部分引发事件。

目前,从线程 1,2,...N 到主线程的切换是由家庭滚动解决方案完成的,这样做是为了避免在整个代码库中锁定代码。这是在 .Net 1.1 中编写的,自迁移到 dotnet core 3.1 以来。我认为可能有一个框架、库、工具、技术等可以为我们处理这个问题,或者只是一个更好的方法。

这是我仍在学习的现有系统。希望这是有道理的。

c# multithreading .net-core backgroundworker
1个回答
4
投票

使用内存数据库是一种选择,只要您愿意将所有引发并发的情况委托给数据库,并且不使用代码执行任何操作。例如,如果您必须根据某些条件更新数据库中的值,则应由数据库检查该条件,而不是由您自己的代码检查。

到处添加锁也是一种选择,这几乎肯定会很快导致代码无法维护。代码可能从一开始就充满了隐藏的错误,随着时间的推移,通常是在最不幸的情况下,您会一一发现这些错误。

你必须意识到你正在处理一个难题,没有任何神奇的解决方案。在多线程应用程序中管理共享状态一直是一个痛苦的根源。

我的建议是将所有这些复杂性封装在线程安全类中,以便应用程序的其余部分可以安全地调用。如何使这些类成为线程安全取决于具体情况。

  1. 使用锁是最灵活的选择,但并不总是最有效的,因为它有可能产生争用。

  2. 使用线程安全集合(例如

    ConcurrentDictionary
    )灵活性较差,因为它们提供的线程安全保证仅限于其内部状态的完整性。例如,如果您必须根据从另一个集合获取的条件来更新一个集合,则仅使用线程安全集合无法使整个操作成为原子操作。另一方面,这些集合比简单的锁提供更好的性能。

  3. 使用不可变集合,例如

    ImmutableQueue
    ,是另一个有趣的选择。它们在内存和 CPU 方面的效率都比并发集合低(添加/删除在许多情况下是 O(Log n) 而不是 O(1)),并且不比它们更灵活,但它们在提供快照方面非常高效主动处理的数据。要以原子方式更新不可变集合,可以使用方便的
    ImmutableInterlocked.Update
    方法。它使用同一集合的更新版本来更新不可变集合的引用,而不使用锁。如果与其他线程发生争用,它可能会多次调用提供的转换,直到赢得比赛。

© www.soinside.com 2019 - 2024. All rights reserved.