如何永远等待直到引发事件? [复制]

Posted

技术标签:

【中文标题】如何永远等待直到引发事件? [复制]【英文标题】:How to wait forever until an event is raised? [duplicate] 【发布时间】:2021-06-27 13:00:33 【问题描述】:

我很确定这有一个相对简单的答案,但不知何故我找不到它。

在处理事件的控制台应用程序示例中(例如https://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html,但我也见过很多其他的)我看到这样的代码:

public static void Main()

        var someObject = new MyClassWithEvents();
        someObject.SomeEventFired += () =>
        
            // do whatever
        ;

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    

现在明显的问题是,如果您超出示例代码(您可以在其中等待按键被按下)并删除 Console.ReadLine() 线程退出(或者如果您不在 Main() 中,您脱离上下文)并且不再处理事件。

我想我可以使用一个永恒的while(true) Sleep() ,但这不会也阻止线程接收事件吗?

编写这种程序的正确方法是什么?这个想法是它看起来很像一个服务,等待在那里什么都不做,直到一个事件被触发。

要了解我的意思,请尝试以下代码:

public static void Main() 
 
  var someObject = new MyClassWithEvents();
  someObject.SomeEventFired += () => 
  
     // do whatever
  ; 

这立即存在!

TIA,

吉姆

【问题讨论】:

旋转等待是您最不想要的。但是让我们假设您找到了一种在没有Console.ReadLine() 的情况下“永远等待”的方法。你的程序应该如何终止?绝不?那么服务可能正是您真正想要的? 那么这可能是你感兴趣的:ManualResetEventSlim 对@RandRandom,谢谢,就是这个意思,这不会立即退出吗?如果第一个事件在几分钟后发生怎么办? 正确的家伙,确切地说,我这些天的具体例子是 rabbitmq 队列,但 FileSystemWatcher 也是一个很好的例子。 是的,那当然是另一回事了。 @Fildor 的水晶球比我的更准确。 :) 【参考方案1】:

尚未对此进行测试,但我会尝试这是最简单的方法:

public static ManualResetEventSlim waiter = new ManualResetEventSlim(false);
public static void Main()

        var someObject = new MyClassWithEvents();
        someObject.SomeEventFired += () =>
        
            // do whatever
        ;
        
        // Just an example for an "Exit-Trigger"
        someObject.ExitProgramEventFired += () =>
        
            waiter.Set(); // Signal the MRE to stop blocking.
        ;

        waiter.Wait(); // Blocks until signalled.
        Console.WriteLine("Exit Program has been triggered.");

        // Cleanup resources ...
    

注意:Wait 方法还带有支持取消和超时的重载,因此您可以真正强化您的设计。

供参考:ManualResetEventSlim

【讨论】:

【参考方案2】:

如果您使用的是较新版本的 .Net,您可以将入口点设为 Task Main 而不是 void Main。然后您可以使用TaskCompletionSource 返回一个任务,该任务将在按下取消键时完成。

这样做应该将主线程释放回线程池,这意味着它应该使用更少的资源。

public static Task Main(string[] args)

    // Set up TaskCompletionSource that completes when the CancelKey is Pressed
    var taskCompletionSource = new TaskCompletionSource<bool>();
    Console.CancelKeyPress += (_, args) =>
    
        args.Cancel = true;
        taskCompletionSource.TrySetResult(true);
    ;

    var someObject = new MyClassWithEvents();
    someObject.SomeEventFired += () => 
    
        // do whatever
    ;

    return taskCompletionSource.Task;

应该提一下,你问'那会不会也阻止线程接收事件',但这不应该是一个问题。回调通常不会在主线程上返回,它们通常由线程池中的随机空闲线程处理。所以即使主线程被什么东西阻塞了,这样的回调通常仍然会运行。

【讨论】:

这只是将事件处理程序包装到任务中还是目的是什么? @user3625699 是的,这只是向 Main() 返回一个任务,除非要求关闭应用程序,否则该任务永远不会完成。主要是返回到 Main 的任务没有完成并且没有使用什么都不做的线程。

以上是关于如何永远等待直到引发事件? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

“未引发语音假设事件”

位图属性更改时如何引发事件? [复制]

Anylogic:如何让代理在队列中等待直到它改变状态? (离散事件流程图)

从自定义集合类中的对象引发事件

操作系统中如何引发进程调度?

如何让程序等待 javascript 中的变量更改?