如果在C#中的某些操作之前收到事件,如何等待?
我有一个类,其中根据从外部应用程序接收到的事件调用方法如果在C#中的某些操作之前收到事件,如何等待?,c#,multithreading,event-handling,C#,Multithreading,Event Handling,我有一个类,其中根据从外部应用程序接收到的事件调用方法 public void ProcessItems(Store id,Items items) { //Some logic UpdateValidItems(id,validItems) } public void UpdateValidItems(Store id,Items items) { //Save in DB } 在处理UpdateValidItems时,外部应用程序可能会调用“Proce
public void ProcessItems(Store id,Items items)
{
//Some logic
UpdateValidItems(id,validItems)
}
public void UpdateValidItems(Store id,Items items)
{
//Save in DB
}
在处理UpdateValidItems时,外部应用程序可能会调用“ProcessItems”。我希望,如果在UpdateValidItems处理期间正在处理UpdateValidItems并调用事件,那么它应该等到UpdateValidItems完成。有什么办法吗
此外,一次可以处理多个存储。所以它应该只等待基于商店的消息。如果storeId不同,则不应等待 我将分离传入事件和处理:
private object lock_object = new object();
public void ProcessItems(Store id,Items items)
{
//Some logic
lock(lock_object)
{
UpdateValidItems(id,validItems)
}
}
- 一次只有一个存储发生变异
- 事件快速返回
- 同一存储的以下事件不会干扰当前处理
编辑/基本示例:
public class Handler
{
private readonly BlockingCollection<QueueEntry> _queue = new BlockingCollection<QueueEntry>();
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
// I used a Form with a button to simulate events, so you'll have to adapt that..
public Handler(Form1 parent)
{
// register for incoming Items
parent.NewItems += Parent_NewItems;
// Start processing on a long running Pool-Thread
Task.Factory.StartNew(QueueWorker, TaskCreationOptions.LongRunning);
}
// Stop Processing
public void Shutdown( bool doitnow )
{
// Mark the queue "complete" - adding is now forbidden.
_queue.CompleteAdding();
// If you want to stop NOW, cancel all operations
if (doitnow ) { _cts.Cancel(); }
// Else the Task will run until the queue has been processed.
}
// This is all that happens on the EDT / Main / UI Thread
private void Parent_NewItems(object sender, NewItemsEventArgs e)
{
try
{
_queue.Add(new QueueEntry { Sender = sender, Event = e });
}
catch (InvalidOperationException)
{
// dontcare ? I didn't - You may, though.
// Will be thrown if the queue has been marked complete.
}
}
private async Task QueueWorker()
{
// While the queue has not been marked complete and is empty
while (!_queue.IsCompleted)
{
QueueEntry entry = null;
try
{
// Wait until an entry is available or until canceled.
entry = _queue.Take(_cts.Token);
}
catch ( OperationCanceledException )
{
// dontcare
}
if (entry != null)
{
await Process(entry, _cts.Token);
}
}
}
private async Task Process(QueueEntry entry, CancellationToken cancel)
{
// Dummy Processing...
await Task.Delay(TimeSpan.FromSeconds(entry.Event.Items), cancel);
}
}
public class QueueEntry
{
public object Sender { get; set; }
public NewItemsEventArgs Event { get; set; }
}
公共类处理程序
{
私有只读BlockingCollection _queue=new BlockingCollection();
私有只读CancellationTokenSource _cts=new CancellationTokenSource();
//我使用了一个带有按钮的表单来模拟事件,所以您必须对其进行调整。。
公共处理程序(Form1父级)
{
//登记进货
parent.NewItems+=parent_NewItems;
//在长时间运行的池线程上开始处理
Task.Factory.StartNew(QueueWorker,TaskCreationOptions.LongRunning);
}
//停止处理
公共无效关闭(bool doitnow)
{
//将队列标记为“完成”-现在禁止添加。
_queue.CompleteAdding();
//如果要立即停止,请取消所有操作
if(doitnow){u cts.Cancel();}
//否则,任务将一直运行,直到处理完队列。
}
//这就是EDT/Main/UI线程上发生的所有事情
私有无效父项\u NewItems(对象发送方,newitemseventergs e)
{
尝试
{
_Add(新的队列条目{Sender=Sender,Event=e});
}
捕获(无效操作异常)
{
//dontcare?我没有,不过你可以。
//如果队列已标记为完成,则将引发。
}
}
专用异步任务QueueWorker()
{
//队列尚未标记为完成且为空
而(!\u queue.IsCompleted)
{
QueueEntry=null;
尝试
{
//等待条目可用或取消。
entry=\u queue.Take(\u cts.Token);
}
捕获(操作取消异常)
{
//唐卡
}
if(条目!=null)
{
等待过程(条目、令牌);
}
}
}
专用异步任务进程(QueueEntry、CancellationToken cancel)
{
//虚拟处理。。。
等待任务。延迟(TimeSpan.FromSeconds(entry.Event.Items),取消);
}
}
公共类队列条目
{
公共对象发送方{get;set;}
公共NewItemsEventArgs事件{get;set;}
}
当然,这可以调整为允许一些并发/并行处理。使用lock()是一个选项吗?我们可以使用lock。检查我在下面发布的答案。它包含了一个例子。我们应该在多种方法中使用这个锁吗?@umer根据您的要求,我认为
lock
不会让您高兴。@Fildor那么什么让我高兴呢?@umer请看我的答案;)锁将有助于确保方法是按顺序折叠的。它在释放锁之前保持对新事件的调用。我认为它完全符合要求。