C# 高性能打破多重嵌套函数的最佳方式

问题描述 投票:0回答:1

我有一个自动化程序用于控制其他应用程序(例如浏览器或桌面应用程序)。 我创建了一个带有“while”的函数来确定应用程序的当前页面或状态,从而决定要执行的下一个操作。

void OuterMostFunc()
{
    while(!exit)
    {
        //InitSomethings...
        InnerFunc();
        //HandleSomethings...
        Thread.Sleep(milliseconds);
    }
}

void InnerFunc()
{
    if(A_Page_Cond)
        A_Func();
    else if(B_Page_Cond)
        B_Func();
    ...
}

但同时,这些应用程序可能会被手动操作或遇到意外事件而改变页面或状态。 因此,我必须检查很多关键点的状态变化,并突破返回最外层函数重新评估情况。 一个相对优雅的解决方案是利用 Try-Catch 以及自定义 BreakException,允许我从代码中的任何位置返回到最外层函数。 此外,我在最外层重置了所有关键变量,因此我不需要担心中断引起的问题。

void OuterMostFunc()
{
    while(!exit)
    {
        //InitSomethings...
        try
        {
            InnerFunc();
        }
        catch(BreakExcetion ex)
        {}
        //HandleSomethings...
        Thread.Sleep(milliseconds);
    }
}

void Any_Deep_Level_Func()
{
    //DoSomethings.
    if(CheckPageIsChanged())
        throw new BreakException();
    //DoSomethings.
}

长期以来,这种方法一直有效且可靠。 然而,由于程序的应用上下文要求最高的速度,Try-Catch带来的性能开销变得难以忽视。 例如,Try-Catch需要数十毫秒的执行时间,对CPU造成了较大的负担。”

如果每个函数都返回bool(或out bool),然后我们在调用每个函数后检查是否返回或执行下一步,确实可以让我们快速返回到最外层函数。 然而,这种方法似乎对代码的可读性造成了灾难,特别是考虑到程序中调用的函数的实际复杂性和多层性质。

补充说明:

  • 优先考虑状态变化后程序的响应能力,性能次之。
c# return try-catch goto
1个回答
0
投票

由于它是同步运行的,因此持续运行的 while 循环本身就会对性能产生不利影响,尤其是在使用

Thread.Sleep
调用时。 不要这样做。切换到异步实现。

以下是在最近的 .NET 版本中使用 BackgroundService 实现长时间运行的应用程序(例如 Windows 服务或 Web 服务)的常见方式:

public sealed class AppService : BackgroundService
{
    // change the update interval as you like, or read from config
    private static readonly _interval = TimeSpan.FromMilliseconds(20);
    private readonly ILogger<Worker> _logger;

    public AppService(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                // use asynchronous check if possible
                // (synchronous might also be fine here)
                if (await CheckChangedAsync(stoppingToken))
                {
                    // do something
                }
            }
            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
            {
                // was canceled
                break;
            }
            catch (Exception exception)
            {
                 _logger.LogError(exception, "Unexpected exception occured");
                 // throw or exit gracefully
                 throw;
            }
            // pause to free resources before the next update
            await Task.Delay(_interval, stoppingToken);
        }
    }
}

注:

  • Thread.Sleep
    仍然会耗尽线程,所以异步方法更好
  • 异步本身非常快,对于你在这里可能做的任何事情来说都足够快了
  • 如果您需要经常进行检查,并且能够实际异步执行它们,您也可以完全摆脱
    _interval
    ,而只依赖于
    CheckChangedAsync
  • 的异步特性
© www.soinside.com 2019 - 2024. All rights reserved.