是否在.NET中维护异步调用的顺序?使用异步等待实现Logger?

时间:2017-02-01 15:44:39

标签: c# async-await

我试图创建一个简单的异步记录器。使日志记录调用非阻塞和尽可能不引人注目的想法。请考虑以下简化代码 -

class Logger {
    public async void Log(string message) {
        LogTable log = new LogTable(); //LogTable is an SqlServer table
        log.Timestamp = DateTime.Now;
        log.Message = message;
        log.SomeProp = SomeVariableTimeCalculation();
        var db = new Entities(); //db is an EF6 context
        db.Entry(log).State = EntityState.Added;
        db.LogTables.Add(log);
        await db.SaveChangesAsync();
   }
}    

可以按如下方式使用它(不等待任何Log(...)调用) -

class Account
    private readonly Logger logger = new Logger();
    public void CreateAccount(Account acc) {
        logger.Log("CreateAccount Start");
        //Maybe some async operations here
        logger.Log("CreateAccount Validation Start");
        bool isValid = Validate(acc);
        logger.Log("CreateAccount Validation End");
        if(isValid) {
            logger.Log("CreateAccount Save Start");
            acc.Save();
            logger.Log("CreateAccount Save End");
        }
        else {
            logger.Log("Account validation failed");
        }
        logger.Log("CreateAccount End");
    }
}

我的问题是 -

  1. 一个接一个地有多个异步Log(...)调用。编译器是否可以选择同时并行运行所有它们?如果是,编译器是否知道维持这些调用的顺序,以便logger.Log("CreateAccount Validation Start");logger.Log("CreateAccount Start");之前不会运行?

  2. 如果编译器没有保留顺序,那么除了在Logger类中维护队列之外,还有其他方法吗?

  3. 更新:澄清 - 我的主要关注点是避免竞争条件,例如,导致logger.Log("CreateAccount Validation Start");logger.Log("CreateAccount Start");之前运行/完成并在不阻止Log的情况下调用CreateAccount }。

3 个答案:

答案 0 :(得分:2)

  

编译器是否可以选择同时并行运行它们?

没有。异步与并行完全不同。

  

如果编译器没有保留顺序,那么除了在Logger类中维护一个队列之外,还有其他办法吗?

编译器确实保留了顺序(即await用于顺序异步代码)。但是,队列仍然不是一个坏主意,因为您可能不想在每次日志记录调用时访问实际数据库。

答案 1 :(得分:0)

除了明确使用任务/线程/并行性之外,async方法总是从它的调用者的角度同步运行,直到它遇到第一个await 1 < / SUP>

因此,Log来电至少等待他们等待SaveChangesAsync完成后才能回复。

1 但是,如果等待的问题已经完成,它可能会在此时继续同步运行。这就是最早的点,它可以将控制权返回给调用者,而不是保证控制权返回点。

答案 2 :(得分:0)

我认为你基本上要问的是这是否会输出1,2,3,...,999,1000

for (int i = 1; i <= 1000; ++i)
{
    logger.Log(i.ToString());
}

简单的答案是否定的。与其他任务相比,每项任务可能并且可能经常执行的执行时间略有不同,例如, 5的任务可以在4的任务完成之前执行。