Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/260.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如果在C#中的某些操作之前收到事件,如何等待?_C#_Multithreading_Event Handling - Fatal编程技术网

如果在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)
    }    
}
  • 让线程等待
  • 事件写入阻塞队列
  • 线程从0。)得到通知,将1“行”(Id和项目)出列
  • 所述线程处理所述项目
  • 线程再次等待,或者如果同时事件添加了更多行:处理队列,直到队列为空,然后再次等待
  • 这可确保:

    • 一次只有一个存储发生变异
    • 事件快速返回
    • 同一存储的以下事件不会干扰当前处理
    您还可以研究如何实现类似的方法


    编辑/基本示例:

    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请看我的答案;)锁将有助于确保方法是按顺序折叠的。它在释放锁之前保持对新事件的调用。我认为它完全符合要求。