using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Text; using System.Threading.Tasks; using Google.Protobuf.WellKnownTypes; namespace ServerCore; //============================================================================================= // 병렬 프로그래밍 환경에서 특정 객체의 Read & Write 처리를 ReaderWriterLockSlim 활용하여 // 동기화해주는 클래스 이다. // // author : kangms // //============================================================================================= public class Synchronizer where TObject : class { private readonly ReaderWriterLockSlim m_rw_lock; private readonly TObject m_value; public Synchronizer(TObject value) { ArgumentNullReferenceCheckHelper.throwIfNull(value, () => $"value is null !!!"); m_value = value; var dynamic_obj = (value as dynamic); ArgumentNullException.ThrowIfNull(dynamic_obj, "dynamic_obj is null !!!"); m_rw_lock = dynamic_obj.getRWLock(); } //============================================================================================= // 호출자의 읽기를 위한 로직을 동기화 해준다. // ReaderWriterLockSlim의 EnterReadLock() 호출하여 동기화 해준다. //============================================================================================= public TResult callReadFunc(Func readFunc) { ArgumentNullReferenceCheckHelper.throwIfNull(readFunc, () => $"readFunc is null !!!"); m_rw_lock.EnterReadLock(); try { return readFunc(m_value); } finally { m_rw_lock.ExitReadLock(); } } //============================================================================================= // 호출자의 쓰기를 위한 로직을 동기화 해준다. // ReaderWriterLockSlim의 EnterWriteLock() 호출하여 동기화 해준다. //============================================================================================= public TResult callWriteFunc(Func writeFunc) { ArgumentNullReferenceCheckHelper.throwIfNull(writeFunc, () => $"writeFunc is null !!!"); m_rw_lock.EnterWriteLock(); try { return writeFunc(m_value); } finally { m_rw_lock.ExitWriteLock(); } } public IDisposable tryReadLock() { m_rw_lock.EnterReadLock(); return new Unlocker(m_rw_lock, isReadLock: true); } public IDisposable tryWriteLock() { m_rw_lock.EnterWriteLock(); return new Unlocker(m_rw_lock, isReadLock: false); } } //============================================================================================= // Synchronizer를 복수개로 처리할 때 사용하는 클래스 이다. // // author : kangms // //============================================================================================= public class MultipleSynchronizer where TObject : class { private readonly ConcurrentDictionary> m_synchronizers = new(); public MultipleSynchronizer(Dictionary toSyncObjects) { foreach (var each in toSyncObjects) { var synchronizer = new Synchronizer(each.Value); m_synchronizers[each.Key] = synchronizer; } } //============================================================================================= // 등록된 객체중에서 locks 파라메터에 의해 선택적으로 Read & Write Lock을 설정해 준다. //============================================================================================= public MultiLock tryLockAll(IEnumerable<(string Key, bool IsReadLock)> locks) { return new MultiLock(m_synchronizers, locks); } //============================================================================================= // 등록된 모든 객체를 Read Lock 설정을 해준다. //============================================================================================= public MultiReadLock tryReadLockAll() { return new MultiReadLock(m_synchronizers); } //============================================================================================= // 등록된 모든 객체를 Write Lock 설정을 해준다. //============================================================================================= public MultiWriteLock tryWriteLockAll() { return new MultiWriteLock(m_synchronizers); } public Synchronizer this[string index] => m_synchronizers[index]; public class MultiLock : IDisposable { private readonly List m_lock_objects; public MultiLock(ConcurrentDictionary> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks) { m_lock_objects = new List(); foreach (var (key, isReadLock) in locks) { if (true == syncObjects.TryGetValue(key, out var found_object)) { m_lock_objects.Add(isReadLock ? found_object.tryReadLock() : found_object.tryWriteLock()); } } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } public class MultiReadLock : IDisposable { private readonly List m_lock_objects; public MultiReadLock(IEnumerable>> syncObjects) { m_lock_objects = new List(); foreach (var each in syncObjects) { m_lock_objects.Add(each.Value.tryReadLock()); } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } public class MultiWriteLock : IDisposable { private readonly List m_lock_objects; public MultiWriteLock(IEnumerable>> syncObjects) { m_lock_objects = new List(); foreach (var each in syncObjects) { m_lock_objects.Add(each.Value.tryWriteLock()); } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } } //============================================================================================= // 병렬 프로그래밍 환경에서 특정 객체의 Read & Write 처리를 ReaderWriterLockSlim 활용하여 // async 기반으로 동기화해주는 클래스 이다. // // author : kangms // //============================================================================================= public class SynchronizerAsync where TObject : class { private readonly ReaderWriterLockSlim m_rw_lock; private readonly TObject m_value; public SynchronizerAsync(TObject value) { m_value = value; m_rw_lock = (value as dynamic).getRWLock(); } //============================================================================================= // 호출자의 읽기를 위한 로직을 async 기반으로 동기화 해준다. // ReaderWriterLockSlim의 tryReadLockAsync() 호출하여 동기화 해준다. //============================================================================================= public async Task callReadFuncAsync(Func> readFunc) { ArgumentNullReferenceCheckHelper.throwIfNull(readFunc, () => $"readFunc is null !!!"); await m_rw_lock.tryReadLockAsync(); try { return await readFunc(m_value); } finally { m_rw_lock.ExitReadLock(); } } //============================================================================================= // 호출자의 쓰기를 위한 로직을 async 기반으로 동기화 해준다. // ReaderWriterLockSlim의 tryWriteLockAsync() 호출하여 동기화 해준다. //============================================================================================= public async Task callWriteFuncAsync(Func> writeFunc) { ArgumentNullReferenceCheckHelper.throwIfNull(writeFunc, () => $"writeFunc is null !!!"); await m_rw_lock.tryWriteLockAsync(); try { return await writeFunc(m_value); } finally { m_rw_lock.ExitWriteLock(); } } public async Task tryReadLockAsync() { return await m_rw_lock.tryReadLockAsync(); } public async Task tryWriteLockAsync() { return await m_rw_lock.tryWriteLockAsync(); } } //============================================================================================= // SynchronizerAsync를 복수개로 처리할 때 사용하는 클래스 이다. // // author : kangms // //============================================================================================= public class MultipleSynchronizerAsync where TObject : class { private readonly ConcurrentDictionary> m_synchronizers = new(); public MultipleSynchronizerAsync(Dictionary toSyncObjects) { foreach (var each in toSyncObjects) { var synchronizer = new SynchronizerAsync(each.Value); m_synchronizers[each.Key] = synchronizer; } } //============================================================================================= // 등록된 객체중에서 locks 파라메터에 의해 선택적으로 Read & Write Lock을 설정해 준다. //============================================================================================= public MultiLockAsync tryLockAllAsync(IEnumerable<(string Key, bool IsReadLock)> locks) { return new MultiLockAsync(m_synchronizers, locks); } //============================================================================================= // 등록된 모든 객체를 Read Lock 설정을 해준다. //============================================================================================= public MultiReadLockAsync tryReadLockAllAsync() { return new MultiReadLockAsync(m_synchronizers); } //============================================================================================= // 등록된 모든 객체를 Write Lock 설정을 해준다. //============================================================================================= public MultiWriteLockAsync tryWriteLockAllAsync() { return new MultiWriteLockAsync(m_synchronizers); } public SynchronizerAsync this[string index] => m_synchronizers[index]; public class MultiLockAsync : IDisposable { private readonly List m_lock_objects; public MultiLockAsync(ConcurrentDictionary> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks) { m_lock_objects = new List(); foreach (var (key, isReadLock) in locks) { if (true == syncObjects.TryGetValue(key, out var found_object)) { m_lock_objects.Add(isReadLock ? found_object.tryReadLockAsync() : found_object.tryWriteLockAsync()); } } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } public class MultiReadLockAsync : IDisposable { private readonly List m_lock_objects; public MultiReadLockAsync(IEnumerable>> syncObjects) { m_lock_objects = new List(); foreach (var each in syncObjects) { m_lock_objects.Add(each.Value.tryReadLockAsync()); } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } public class MultiWriteLockAsync : IDisposable { private readonly List m_lock_objects; public MultiWriteLockAsync(IEnumerable>> syncObjects) { m_lock_objects = new List(); foreach (var each in syncObjects) { m_lock_objects.Add(each.Value.tryWriteLockAsync()); } } public void Dispose() { foreach (var lock_obj in m_lock_objects) { lock_obj.Dispose(); } } } } //============================================================================================= // Unlocker가 소멸될 때 ReaderWriterLockSlim가 Exit 될 수 있도록 해주는 클래스 이다. //============================================================================================= public class Unlocker : IDisposable { private readonly ReaderWriterLockSlim m_ref_rw_lock; private readonly bool m_is_read_lock; public Unlocker(ReaderWriterLockSlim readWriteLockObject, bool isReadLock) { m_ref_rw_lock = readWriteLockObject; m_is_read_lock = isReadLock; } public void Dispose() { if (m_is_read_lock) { m_ref_rw_lock.ExitReadLock(); } else { m_ref_rw_lock.ExitWriteLock(); } } }