如何将突发事件缓冲为较少的操作
本文关键字:操作 缓冲 突发事件 | 更新日期: 2023-09-27 18:00:43
我想将多个事件减少为一个延迟的操作。在一些触发发生后,我预计会出现一些更相似的触发,但我不想重复由此产生的延迟操作。动作等待,以便给爆裂一个完成的机会
问题是:我如何才能以一种优雅的可重复使用的方式做到这一点
到目前为止,我使用了一个属性来标记事件并触发延迟操作,如下所示:
public void SomeMethod()
{
SomeFlag = true; //this will intentionally return to the caller before completing the resulting buffered actions.
}
private bool someFlag;
public bool SomeFlag
{
get { return someFlag; }
set
{
if (someFlag != value)
{
someFlag = value;
if (value)
SomeDelayedMethod(5000);
}
}
}
public async void SomeDelayedMethod(int delay)
{
//some bufferred work.
await Task.Delay(delay);
SomeFlag = false;
}
下面是一个较短的方法,但仍然不是通用的或可重复使用的。。。我想要一些简洁的东西来打包操作和标志,并保留功能(在执行完成之前返回给调用者(就像今天一样))我还需要能够将对象引用传递给此操作)
public void SerializeAccountsToConfig()
{
if (!alreadyFlagged)
{
alreadyFlagged = true;
SerializeDelayed(5000, Serialize);
}
}
public async void SerializeDelayed(int delay, Action whatToDo)
{
await Task.Delay(delay);
whatToDo();
}
private bool alreadyFlagged;
private void Serialize()
{
//some buferred work.
//string json = JsonConvert.SerializeObject(Accounts, Formatting.Indented);
//Settings1.Default.Accounts = json;
//Settings1.Default.Save();
alreadyFlagged = false;
}
这里有一个线程安全且可重用的解决方案。
您可以创建DelayedSingleAction
的实例,并在构造函数中传递您想要执行的操作。我相信这是线程安全的,尽管在开始操作之前会重新启动计时器的风险很小,但我认为无论解决方案是什么,这种风险都会存在
public class DelayedSingleAction
{
private readonly Action _action;
private readonly long _millisecondsDelay;
private long _syncValue = 1;
public DelayedSingleAction(Action action, long millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay;
}
private Task _waitingTask = null;
private void DoActionAndClearTask(Task _)
{
Interlocked.Exchange(ref _syncValue, 1);
_action();
}
public void PerformAction()
{
if (Interlocked.Exchange(ref _syncValue, 0) == 1)
{
_waitingTask = Task.Delay(TimeSpan.FromMilliseconds(_millisecondsDelay))
.ContinueWith(DoActionAndClearTask);
}
}
public Task Complete()
{
return _waitingTask ?? Task.FromResult(0);
}
}
有关从多个线程连续调用一个操作的示例,请参阅此dotnetfiddle。
https://dotnetfiddle.net/el14wZ
由于您对RX感兴趣,这里的简单控制台应用程序示例:
static void Main(string[] args)
{
// event source
var burstEvents = Observable.Interval(TimeSpan.FromMilliseconds(50));
var subscription = burstEvents
.Buffer(TimeSpan.FromSeconds(3)) // collect events 3 seconds
//.Buffer(50) // or collect 50 events
.Subscribe(events =>
{
//Console.WriteLine(events.First()); // take only first event
// or process event collection
foreach (var e in events)
Console.Write(e + " ");
Console.WriteLine();
});
Console.ReadLine();
return;
}
基于Andrew提出的解决方案,这里有一个更通用的解决方案
延迟操作的声明和实例创建:
public DelayedSingleAction<Account> SendMailD;
在函数内部或构造函数中创建实例(这可以是每个操作在不同对象上的操作的集合):
SendMailD = new DelayedSingleAction<Account>(SendMail, AccountRef, 5000);
反复调用此操作
SendMailD.PerformAction();
发送邮件是你将"突发控制"的行动。其签名匹配:
public int SendMail(Account A)
{}
这是更新后的类
public class DelayedSingleAction<T>
{
private readonly Func<T, int> actionOnObj;
private T tInstance;
private readonly long millisecondsDelay;
private long _syncValue = 1;
public DelayedSingleAction(Func<T, int> ActionOnObj, T TInstance, long MillisecondsDelay)
{
actionOnObj = ActionOnObj;
tInstance = TInstance;
millisecondsDelay = MillisecondsDelay;
}
private Task _waitingTask = null;
private void DoActionAndClearTask(Task _)
{
Console.WriteLine(string.Format("{0:h:mm:ss.fff} DelayedSingleAction Resetting SyncObject: Thread {1} for {2}", DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId, tInstance));
Interlocked.Exchange(ref _syncValue, 1);
actionOnObj(tInstance);
}
public void PerformAction()
{
if (Interlocked.Exchange(ref _syncValue, 0) == 1)
{
Console.WriteLine(string.Format("{0:h:mm:ss.fff} DelayedSingleAction Starting the timer: Thread {1} for {2}", DateTime.Now, System.Threading.Thread.CurrentThread.ManagedThreadId, tInstance));
_waitingTask = Task.Delay(TimeSpan.FromMilliseconds(millisecondsDelay)).ContinueWith(DoActionAndClearTask);
}
}
public Task Complete()
{
return _waitingTask ?? Task.FromResult(0);
}
}