Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在C语言中处理多个并发交互实体#_C#_.net_Concurrency - Fatal编程技术网

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));
回归未来;
}
// ...
}

还有什么其他选择我没有考虑?有什么好办法吗?我应该这么做吗?

你所有的选择都会给你带来痛苦

  • 即使代码在技术上是正确的,您也会在您的域中体验到逻辑竞争条件。类似订单的东西可以在付款前发货
  • 这会损害可维护性。线程代码很难调试、测试和阅读。当它与应用程序交错时,这会变得更加复杂
  • 正确的方法是将线程代码与应用程序代码完全分离。将任务放在单线程光纤中。任务跨所有相关实体同步执行所有作业。任务完成后,可以异步执行IO。
    我已经为这种方法写过书

    您可以将突变排列到拥有的光纤中,然后将其继续排列到您自己的光纤中。这样您就没有任何显式锁

    但是:这种光纤方法并不比在访问实体之前只使用
    锁好多少。(锁内部包含一个队列。)


    此外,您不能以这种方式进行跨实体交易。使用锁方法,您可以收集参与事务的所有实体的锁,将它们按总顺序排序并全部锁定。这使您可以进行无死锁的跨实体事务。

    目前,我或多或少地选择了选项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运行一次更新,所以同步更新每个实体确实会影响性能。重要的是,如果发生以下情况,其他实体不会受到影响