我正在开发一个应用程序,该应用程序具有许多可以同时交互的不同实体。我想知道这些实体以线程安全的方式相互交互的最佳方式是什么。
为了用一些简化的代码进行演示,请考虑每个实体都有自己的光纤和一些状态:
class Fiber
{
private ActionBlock<Action> _workQueue;
public Fiber()
{
_workQueue = new ActionBlock<Action>((a) => a());
}
public void Enqueue(Action a)
{
_workQueue.Post(a);
}
public void Stop()
{
_workQueue.Complete();
}
}
class EntityState
{
public int x { get; set; }
}
class Entity
{
private Fiber _fiber = new Fiber();
public EntityState State { get; set; }
// ...
}
假设操作被任意排队到实体纤程上。一个这样的 Action 可能是一个实体必须修改另一个实体的状态。我考虑过两种选择以线程安全的方式执行此操作。
选项 1:仅允许通过线程安全包装器进行状态突变,I.E.
class Entity
{
private Fiber _fiber = new Fiber();
private ReaderWriterLockSlim _stateLock = new ReaderWriterLockSlim();
private EntityState _state = new EntityState();
public T ReadState<T>(Func<EntityState, T> reader)
{
T result = default(T);
_stateLock.EnterReadLock();
result = reader(_state);
_stateLock.ExitReadLock();
return result;
}
public void WriteState(Action<EntityState> writer)
{
_stateLock.EnterWriteLock();
writer(_state);
_stateLock.ExitWriteLock();
}
// ...
}
选项 2:仅通过将其调度到拥有实体的纤程上并返回 Future 来允许状态突变,以便突变者可以看到突变何时发生,即
class Future<T>
{
public T Value { get; set; }
}
class Entity
{
private Fiber _fiber = new Fiber();
private EntityState _state = new EntityState();
public Future<T> AccessState<T>(Func<EntityState, T> accessor)
{
Future<T> future = new Future<T>();
_fiber.Enqueue(() => future.Value = accessor(_state));
return future;
}
// ...
}
我还没有考虑过哪些其他选择?有没有好的方法可以做到这一点?我到底应该这样做吗?
最佳答案
你的所有选择都会给你带来痛苦。
- 即使代码在技术上是正确的,您也会在域中遇到逻辑竞争条件。像订单这样的东西可以在付款之前发货。
- 这会损害可维护性。线程代码很难调试、测试、阅读。当它与应用程序交错时,情况会变得更加复杂。
正确的方法是将线程代码与应用程序代码完全分离。将任务放入单线程纤程中。任务在所有涉及的实体之间同步执行所有作业。任务完成后,就可以异步执行IO了。 我写过a library对于这种方法。
关于c# - 如何在 C# 中处理多个并发交互的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17749139/