我试图创建一个简单的异步记录器。使日志记录调用非阻塞和尽可能不引人注目的想法。请考虑以下简化代码 -
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");
}
}
我的问题是 -
一个接一个地有多个异步Log(...)
调用。编译器是否可以选择同时并行运行所有它们?如果是,编译器是否知道维持这些调用的顺序,以便logger.Log("CreateAccount Validation Start");
在logger.Log("CreateAccount Start");
之前不会运行?
如果编译器没有保留顺序,那么除了在Logger类中维护队列之外,还有其他方法吗?
更新:澄清 - 我的主要关注点是避免竞争条件,例如,导致logger.Log("CreateAccount Validation Start");
在logger.Log("CreateAccount Start");
之前运行/完成并在不阻止Log
的情况下调用CreateAccount
}。
答案 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的任务完成之前执行。