using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Base;
namespace Model
{
public enum LockStatus
{
LockedNot,
LockRequesting,
Locked,
}
///
/// 分布式锁组件,Unit对象可能在不同进程上有镜像,访问该对象的时候需要对他加锁
///
[EntityEvent(EntityEventId.LockComponent)]
public class LockComponent: Component
{
private LockStatus status = LockStatus.LockedNot;
private string address;
private int lockCount;
private readonly Queue> queue = new Queue>();
public void Awake(string addr)
{
this.address = addr;
}
public async Task Lock()
{
++this.lockCount;
if (this.status == LockStatus.Locked)
{
return;
}
if (this.status == LockStatus.LockRequesting)
{
await WaitLock();
return;
}
this.status = LockStatus.LockRequesting;
// 真身直接本地请求锁,镜像需要调用Rpc获取锁
MasterComponent masterComponent = this.GetComponent();
if (masterComponent != null)
{
await masterComponent.Lock(this.address);
}
else
{
RequestLock();
await WaitLock();
}
}
private Task WaitLock()
{
TaskCompletionSource tcs = new TaskCompletionSource();
if (this.status == LockStatus.Locked)
{
tcs.SetResult(true);
return tcs.Task;
}
this.queue.Enqueue(tcs);
return tcs.Task;
}
private async void RequestLock()
{
try
{
Session session = Game.Scene.GetComponent().Get(this.address);
string serverAddress = Game.Scene.GetComponent().StartConfig.ServerIP;
G2G_LockRequest request = new G2G_LockRequest { Id = this.Owner.Id, Address = serverAddress };
await session.Call(request);
this.status = LockStatus.Locked;
foreach (TaskCompletionSource taskCompletionSource in this.queue)
{
taskCompletionSource.SetResult(true);
}
this.queue.Clear();
}
catch (Exception e)
{
Log.Error($"获取锁失败: {this.address} {this.Owner.Id} {e}");
}
}
public async Task Release()
{
--this.lockCount;
if (this.lockCount != 0)
{
return;
}
this.status = LockStatus.LockedNot;
Session session = Game.Scene.GetComponent().Get(this.address);
G2G_LockReleaseRequest request = new G2G_LockReleaseRequest();
await session.Call(request);
}
}
}