435 lines
14 KiB
C#
435 lines
14 KiB
C#
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<TObject>
|
|
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<TResult>(Func<TObject, TResult> 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<TResult>(Func<TObject, TResult> 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<TObject>
|
|
where TObject : class
|
|
{
|
|
private readonly ConcurrentDictionary<string, Synchronizer<TObject>> m_synchronizers = new();
|
|
|
|
public MultipleSynchronizer(Dictionary<string, TObject> toSyncObjects)
|
|
{
|
|
foreach (var each in toSyncObjects)
|
|
{
|
|
var synchronizer = new Synchronizer<TObject>(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<TObject> this[string index] => m_synchronizers[index];
|
|
|
|
|
|
public class MultiLock : IDisposable
|
|
{
|
|
private readonly List<IDisposable> m_lock_objects;
|
|
|
|
public MultiLock(ConcurrentDictionary<string, Synchronizer<TObject>> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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<IDisposable> m_lock_objects;
|
|
|
|
public MultiReadLock(IEnumerable<KeyValuePair<string, Synchronizer<TObject>>> syncObjects)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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<IDisposable> m_lock_objects;
|
|
|
|
public MultiWriteLock(IEnumerable<KeyValuePair<string, Synchronizer<TObject>>> syncObjects)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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<TObject>
|
|
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<TResult> callReadFuncAsync<TResult>(Func<TObject, Task<TResult>> 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<TResult> callWriteFuncAsync<TResult>(Func<TObject, Task<TResult>> 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<IDisposable> tryReadLockAsync()
|
|
{
|
|
return await m_rw_lock.tryReadLockAsync();
|
|
}
|
|
|
|
public async Task<IDisposable> tryWriteLockAsync()
|
|
{
|
|
return await m_rw_lock.tryWriteLockAsync();
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
// SynchronizerAsync를 복수개로 처리할 때 사용하는 클래스 이다.
|
|
//
|
|
// author : kangms
|
|
//
|
|
//=============================================================================================
|
|
|
|
public class MultipleSynchronizerAsync<TObject>
|
|
where TObject : class
|
|
{
|
|
private readonly ConcurrentDictionary<string, SynchronizerAsync<TObject>> m_synchronizers = new();
|
|
|
|
public MultipleSynchronizerAsync(Dictionary<string, TObject> toSyncObjects)
|
|
{
|
|
foreach (var each in toSyncObjects)
|
|
{
|
|
var synchronizer = new SynchronizerAsync<TObject>(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<TObject> this[string index] => m_synchronizers[index];
|
|
|
|
|
|
public class MultiLockAsync : IDisposable
|
|
{
|
|
private readonly List<IDisposable> m_lock_objects;
|
|
|
|
public MultiLockAsync(ConcurrentDictionary<string, SynchronizerAsync<TObject>> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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<IDisposable> m_lock_objects;
|
|
|
|
public MultiReadLockAsync(IEnumerable<KeyValuePair<string, SynchronizerAsync<TObject>>> syncObjects)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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<IDisposable> m_lock_objects;
|
|
|
|
public MultiWriteLockAsync(IEnumerable<KeyValuePair<string, SynchronizerAsync<TObject>>> syncObjects)
|
|
{
|
|
m_lock_objects = new List<IDisposable>();
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|