Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/284.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#_.net_Wpf_Multithreading - Fatal编程技术网

C# 如何制作线程安全的事件处理程序

C# 如何制作线程安全的事件处理程序,c#,.net,wpf,multithreading,C#,.net,Wpf,Multithreading,在我的应用程序中,我有一个队列,每当队列发生任何更改时都会触发通知,但有时,当队列事件处理程序上同时有操作时,它会触发多次,这没关系,但我不希望 下面是事件处理程序的代码: private async void NotificationQueue_Changed(object sender, EventArgs e) { if (!IsQueueInProcess) await ProcessQeueue(); } 在ProcessQueue方法中,我将IsQueueInProce

在我的应用程序中,我有一个队列,每当队列发生任何更改时都会触发通知,但有时,当队列事件处理程序上同时有操作时,它会触发多次,这没关系,但我不希望

下面是事件处理程序的代码:

private async void NotificationQueue_Changed(object sender, EventArgs e)
{
  if (!IsQueueInProcess)
    await ProcessQeueue();
}

ProcessQueue
方法中,我将
IsQueueInProcess
设置为true,并且每当它完成时,就会设置为false。现在的问题是,每当多个事件通知同时触发时,就会开始执行多个
processqueue
方法,这是我不希望看到的。我想确保在任何给定的时间只有一次执行
ProcessQUEUEUE

如果您声明每当队列发生任何更改时都会引发此事件,并且队列可以同时使用(即有多个生产者向队列添加内容),在我看来,解决这一问题的最佳方法可能是完全放弃基于事件的行为。相反,使用
BlockingCollection
,使用一个专用于通过
GetConsumingEnumerable()
处理队列的线程。只要队列为空,该方法就会阻止线程,并允许线程在任何其他线程向队列添加内容时删除和处理队列中的项目。集合本身是线程安全的,因此使用它您不需要任何额外的线程同步(对于队列的处理,也就是说……处理一个项目可能涉及线程交互,但您的问题中没有描述这一方面的内容,所以我不能说这方面的任何方式)

也就是说,从字面上看,最简单的方法是包含一个信号量:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);

private async void NotificationQueue_Changed(object sender, EventArgs e)
{
    if (_semaphore.Wait(0))
    {
        await ProcessQueue();
        _semaphore.Release();
    }
}
上面尝试获取信号量的锁。超时为0毫秒时,即使无法获取信号量,它也会立即返回。返回值指示是否成功获取信号量

这样,只要没有未完成的队列处理操作,当前事件处理程序调用就可以获取信号量并调用
ProcessQueue()
方法。当该操作完成时,continuation将释放信号量。在此之前,事件处理程序的其他调用将无法获取信号量,因此不会启动队列的处理


我将注意到,这里没有任何东西可以保证线程之间相互竞争的解决方案,从而确保队列总是空的,或者总是有一些处理操作作用于它。这取决于您,以确保
ProcessQueue()
方法具有所需的同步,以确保如果任何线程修改了队列并引发了此事件,那么如果第一轮无法观察到更改,该线程将不会无法启动另一轮处理

或者换句话说,您需要确保对于要引发该事件的任何线程,当前处理操作将观察其对队列的更改,或者该线程将启动一个新的线程

你的问题中没有足够的背景,任何人都无法具体解决这个问题。我只想指出,在尝试实现这种系统时,这是一件很常见的事情,有人可能会忽视这一点。总之,我们更有理由使用
BlockingCollection
来使用添加到队列中的元素,使用一个专用线程



另见相关问题。这是一个稍微不同的问题,因为接受的答案会导致事件处理程序的每次调用导致事件处理程序启动的操作。您的场景更简单,因为您只是想跳过新操作的启动,但您仍然可以在那里找到一些有用的见解。

我同意Peter的观点,放弃基于事件的通知是最好的解决方案,您应该转移到生产者/消费者队列。但是,我建议使用一个TPL数据流块,而不是
BlockingCollection

特别是,
ActionBlock
应该可以很好地工作:

private readonly ActionBlock<T> notificationQueue = new ActionBlock<T>(async t =>
{
  await ProcessQueueItem(t);
});
private readonly ActionBlock notificationQueue=new ActionBlock(异步t=>
{
等待处理队列项(t);
});

默认情况下,TPL数据流块的并发限制为1。

是否有另一个布尔值指示它当前是否正在执行?请查看支持页。@zee尝试将IsQueueInProcess设置为静态。您不能将
NotificationQueue\u Changed()
设置为同步方法吗?这个
await ProcessQueue()
没有多大意义,因为调用方法本身是一个fire-and-forget方法(使用无法等待的异步void)。IsQueueInProcess仅是静态的,但问题是有时会一次引发一些多个NotificationQueue_Changed事件,并且与在ProcessQeue中IsQueueInProcess获得设置为true的更改之前相比,多个ProcessQeue开始执行。我不想使ProcessQUEUE同步,只想在运行时忽略事件