C# 如何在C语言中处理多个并发交互实体#
我正在开发一个应用程序,它有许多可以并发交互的不同实体。我想知道那些实体以线程安全的方式相互交互的最佳方式是什么C# 如何在C语言中处理多个并发交互实体#,c#,.net,concurrency,C#,.net,Concurrency,我正在开发一个应用程序,它有许多可以并发交互的不同实体。我想知道那些实体以线程安全的方式相互交互的最佳方式是什么 用一些简化的代码演示,考虑每个实体都有它自己的光纤和一些状态: class Fiber { private ActionBlock<Action> _workQueue; public Fiber() { _workQueue = new ActionBlock<Action>((a) => a());
用一些简化的代码演示,考虑每个实体都有它自己的光纤和一些状态:
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; }
// ...
}
类光纤
{
私有ActionBlock\u工作队列;
公共光纤()
{
_工作队列=新动作块((a)=>a());
}
公共无效排队(操作a)
{
_工作队列。Post(a);
}
公共停车场()
{
_workQueue.Complete();
}
}
类实体状态
{
公共整数x{get;set;}
}
类实体
{
专用光纤_Fiber=新光纤();
公共EntityState状态{get;set;}
// ...
}
假设操作任意排队到实体光纤上。一个这样的行为可能是一个实体必须修改另一个实体的状态。我考虑过两种方法来实现线程安全
选项1:只允许通过线程安全包装器进行状态变异,即
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();
}
// ...
}
类实体
{
专用光纤_Fiber=新光纤();
私有ReaderWriterLockSlim _stateLock=new ReaderWriterLockSlim();
私有实体状态_state=新实体状态();
公共T读状态(Func读卡器)
{
T结果=默认值(T);
_stateLock.EnterReadLock();
结果=读卡器(_状态);
_stateLock.exitradlock();
返回结果;
}
公共财产(诉讼撰写人)
{
_stateLock.EnterWriteLock();
作者(州);
_stateLock.ExitWriteLock();
}
// ...
}
选项2:只允许状态变异,将其调度到拥有实体的光纤上,并返回未来,以便变异者可以看到何时发生变异,即
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;
}
// ...
}
class未来
{
公共T值{get;set;}
}
类实体
{
专用光纤_Fiber=新光纤();
私有实体状态_state=新实体状态();
公共未来访问状态(Func访问器)
{
未来=新的未来();
_fiber.Enqueue(()=>future.Value=accessor(_state));
回归未来;
}
// ...
}
还有什么其他选择我没有考虑?有什么好办法吗?我应该这么做吗?你所有的选择都会给你带来痛苦
我已经为这种方法写过书 您可以将突变排列到拥有的光纤中,然后将其继续排列到您自己的光纤中。这样您就没有任何显式锁 但是:这种光纤方法并不比在访问实体之前只使用
锁好多少。(锁内部包含一个队列。)
此外,您不能以这种方式进行跨实体交易。使用锁方法,您可以收集参与事务的所有实体的锁,将它们按总顺序排序并全部锁定。这使您可以进行无死锁的跨实体事务。目前,我或多或少地选择了选项2,但如果访问状态不会阻塞,我会同步设置结果。即
using System;
using System.Threading;
namespace Server.Utility
{
public class ThreadSafeWrapper<ObjectType>
{
private ObjectType m_object;
private Fiber m_fiber;
public ThreadSafeWrapper(ObjectType obj, Fiber fiber)
{
m_object = obj;
m_fiber = fiber;
}
public Future<ReturnType> Transaction<ReturnType>(Func<ObjectType, ReturnType> accessor)
{
Future<ReturnType> future = new Future<ReturnType>();
ReturnType synchronousResult = default(ReturnType);
if (Monitor.TryEnter(m_object))
{
synchronousResult = accessor(m_object);
Monitor.Exit(m_object);
future.SetResult(synchronousResult);
}
else
{
m_fiber.Enqueue(() =>
{
ReturnType result = default(ReturnType);
lock (m_object)
{
result = accessor(m_object);
}
future.SetResult(result);
});
}
return future;
}
public void Transaction(Action<ObjectType> accessor)
{
if (Monitor.TryEnter(m_object))
{
accessor(m_object);
Monitor.Exit(m_object);
}
else
{
m_fiber.Enqueue(() =>
{
lock (m_object)
{
accessor(m_object);
}
});
}
}
}
}
使用系统;
使用系统线程;
命名空间服务器.Utility
{
公共类ThreadSafeWrapper
{
私有对象类型m_对象;
专用光纤m_光纤;
公共ThreadSafeWrapper(ObjectType obj,光纤)
{
m_object=obj;
m_纤维=纤维;
}
公共未来事务(Func访问器)
{
未来=新的未来();
ReturnType synchronousResult=默认值(ReturnType);
if(监视器TryEnter(m_对象))
{
synchronousResult=访问器(m_对象);
监视器。退出(m_对象);
future.SetResult(synchronousResult);
}
其他的
{
m_光纤排队(()=>
{
ReturnType结果=默认值(ReturnType);
锁定(m_对象)
{
结果=访问器(m_对象);
}
future.SetResult(result);
});
}
回归未来;
}
公共作废事务(操作访问器)
{
if(监视器TryEnter(m_对象))
{
存取器(m_对象);
监视器。退出(m_对象);
}
其他的
{
m_光纤排队(()=>
{
锁定(m_对象)
{
存取器(m_对象);
}
});
}
}
}
}
感谢您的输入。我或多或少都在做你提到的事情(见我的答案)。我使用光纤是因为每个实体都使用异步套接字从网络流接收数据。我将它们安排在光纤上,以确保它们按照到达的顺序进行处理。谢谢您的输入。你的两个观点都是绝对正确的,我已经开始觉得编写代码就像穿越地雷一样;)。该项目是一个游戏服务器,这就是我为什么选择高并发性的原因。我的目标是每50ms运行一次更新,所以同步更新每个实体确实会影响性能。重要的是,如果发生以下情况,其他实体不会受到影响