Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.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# 在Windows服务内部进行轮询的常见做法_C#_.net_Multithreading_Windows Services_Long Polling - Fatal编程技术网

C# 在Windows服务内部进行轮询的常见做法

C# 在Windows服务内部进行轮询的常见做法,c#,.net,multithreading,windows-services,long-polling,C#,.net,Multithreading,Windows Services,Long Polling,通常建议您使用类似的方法(使用超时): 其中,Poll函数的定义如下: private void Poll() { try { var timeout = Int32.Parse(ConfigurationManager.AppSettings["pollingTimeout"]); while(!this.finishedEvent.WaitOne(timeout, false)) { // do polling

通常建议您使用类似的方法(使用超时):

其中,
Poll
函数的定义如下:

private void Poll() {
    try {
        var timeout = Int32.Parse(ConfigurationManager.AppSettings["pollingTimeout"]);
        while(!this.finishedEvent.WaitOne(timeout, false)) {
            // do polling
        }
    }
    catch(Exception ex) { 
        Logger.Log.Fatal(ex); 
        throw; 
    }
}
  • 这些结构是否基本相同:

    while(!this.finishedEvent.WaitOne(0,false))

    while(true)
    没有
    finishedEvent

  • 我已经读到,超时是用来减少cpu的使用。使用不超时的轮询是一个错误的选择吗

  • 有一种非常简单的方法可以做到这一点,前提是您不需要严格有序地关闭。如果将
    workerThread
    标记为,则当服务停止时,它将自动关闭。在本例中,您可以放弃使用
    finishedEvent
    ,而使用无限循环。比如说,

    Thread workerThread = null;
    
    protected override void OnStart(string[] args)
    {
        this.workerThread = new Thread(this.DoWork);
        // Signal the thread to stop automatically when the process exits.
        this.workerThread.IsBackground = true;
        this.workerThread.Start();
    }
    
    protected override void OnStop()
    {
    }
    
    private void DoWork()
    {
        try
        {
            while (true)
            {
                // do your work here...
            }
        }
        catch (Exception ex)
        {
            // handle exception here...
        }
    }
    
    请注意,只有当您正在进行的工作在任何时候都可以中断而不会产生不良影响时,才应使用此方法。让我们举一个例子,您正在将数据写入Excel电子表格。一旦Windows服务退出,由
    DoWork()
    方法表示的线程也将立即退出。如果是在向电子表格添加数据的中间,电子表格很可能会有不完整的信息,或者更糟的情况下,甚至可能处于无法在Excel中打开的状态。关键是,这种方法可以使用,但只能在某些情况下使用

    更好的方法是完全过渡到基于事件的机制。它比轮询更有效率,并且允许有序地关闭您的服务。下面是一个带有注释的示例

    Thread _workThread = null;
    // I use ManualResetEvent instead of AutoResetEvent because I do NOT want
    // this event to EVER reset.  It is meant to be set exactly one time.
    ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
    
    protected override void OnStart(string[] args)
    {
        _workThread = new Thread(DoWork());
        _workThread.Start();
    }
    
    protected override void OnStop()
    {
        // Trigger the DoWork() method, i.e., the _workThread, to exit.
        _shutdownEvent.Set();
    
        // I always shutdown my service by simply joining the work thread.
        // There are probably more advanced techniques that take into account
        // longer shutdown cycles, but I design my worker thread(s) to have
        // tight work cycles so that the shutdownEvent is examined frequently
        // enough to facilitate timely shutdowns.
        _workThread.Join();
    }
    
    现在让我们看一下
    DoWork()
    方法的详细信息。对于这个例子,我将使用一个计时器来说明基于事件的方法。请注意,此图与调用带有超时的
    WaitOne()
    方法没有本质区别。然而,如果要完成的工作涉及处理来自其他线程的输入,例如,从网络套接字接收数据的线程或从数据库读取数据的线程,那么这种方法很容易适应这些场景

    // Creature of habit.  AutoResetEvent would probably work for this event,
    // but I prefer to manually control when the event resets.
    ManualResetEvent _timerElapsedEvent = new ManualResetEvent(false);
    System.Timers.Timer _timer = null;
    
    private void DoWork() {
        try {
            // Create, configure, and start the timer to elapse every second and
            // require a manual restart (again, I prefer the manual control).
            // Note when the timer elapses, it sets the _timerElapsedEvent.
            _timer = new Timer(1000) { AutoReset = false };
            _timer.Elapsed =+ (sender, e) => _timerElapsedEvent.Set();
            _timer.Start();
    
            // Create a WaitHandle array that contains the _shutdownEvent and
            // the _timerElapsedEvent...in that order!
            WaitHandle[] handles = new WaitHandle[] { _shutdownEvent, _timerElapsedEvent };
    
            // Employ the event-based mechanism.
            while (!_shutdownEvent.WaitOne(0)) {
                switch (WaitHandle.WaitAny(handles) {
                    case 0:
                        // This case handles when the _shutdownEvent occurs,
                        // which will cause the while loop to exit.
                        break;
                    case 1:
                        // This case handles when the _timerElapsedEvent occurs.
                        // Do the work, reset the event, and restart the timer.
                        DoProcessing();
                        _timerElapsedEvent.Reset();
                        _timer.Start();
                        break;
                }
            }
        } catch (Exception ex) {
            // handle exception here...
        }
    }
    
    数组使基于事件的机制成为可能。创建阵列时,始终确保按优先级顺序将事件添加到阵列中。这就是为什么
    \u shutdownEvent
    列在
    \u timeredevent
    之前的原因。如果阵列中的事件被反转,则可能永远不会处理
    \u shutdownEvent
    。您可以根据需要向
    WaitHandle
    数组添加任意数量的事件。这就是这种方法如此灵活的原因

    最后一个想法。为便于及时关闭服务,您需要确保触发
    \u timeRecursedEvent
    时要完成的工作不会花费太长时间。换句话说,
    \u shutdownEvent
    将不会被
    while
    循环检查,直到
    DoProcessing()
    方法退出。因此,您需要限制在
    DoProcessing()
    方法中花费的时间。如果该方法是长期运行的,那么您可能需要检查
    DoProcessing()
    中的
    \u shutdownEvent
    ,并在服务指示要关闭时在关键点退出


    希望这有帮助。

    不,它们不是等效的。您永远无法使用while(true)循环停止服务。你会燃烧100%的核心,什么也没做。投票是不好的,但有时你就是没有选择,因为你不能让事件告诉你发生了什么有趣的事情。视情况而定。好吧,我明白了!因此,当您在单独的(工作)线程上时,使用
    AutoResetEvent
    是向服务发出停止/启动信号的方式。非常好的解释!谢谢你的回答。愿上帝以耶稣的名义保佑你!你也是,兄弟
    // Creature of habit.  AutoResetEvent would probably work for this event,
    // but I prefer to manually control when the event resets.
    ManualResetEvent _timerElapsedEvent = new ManualResetEvent(false);
    System.Timers.Timer _timer = null;
    
    private void DoWork() {
        try {
            // Create, configure, and start the timer to elapse every second and
            // require a manual restart (again, I prefer the manual control).
            // Note when the timer elapses, it sets the _timerElapsedEvent.
            _timer = new Timer(1000) { AutoReset = false };
            _timer.Elapsed =+ (sender, e) => _timerElapsedEvent.Set();
            _timer.Start();
    
            // Create a WaitHandle array that contains the _shutdownEvent and
            // the _timerElapsedEvent...in that order!
            WaitHandle[] handles = new WaitHandle[] { _shutdownEvent, _timerElapsedEvent };
    
            // Employ the event-based mechanism.
            while (!_shutdownEvent.WaitOne(0)) {
                switch (WaitHandle.WaitAny(handles) {
                    case 0:
                        // This case handles when the _shutdownEvent occurs,
                        // which will cause the while loop to exit.
                        break;
                    case 1:
                        // This case handles when the _timerElapsedEvent occurs.
                        // Do the work, reset the event, and restart the timer.
                        DoProcessing();
                        _timerElapsedEvent.Reset();
                        _timer.Start();
                        break;
                }
            }
        } catch (Exception ex) {
            // handle exception here...
        }
    }