【问题标题】: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 的水晶球比我的更准确。 :)

标签: c# multithreading events


【解决方案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 的任务没有完成并且没有使用什么都不做的线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    • 2012-10-19
    • 2013-11-24
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多