我看到使用和不使用来自调用者的await
之间存在巨大的性能差异。我觉得只有回报才有区别。即,如果我使用await
,那么调用方法应该在返回到下一个语句之前等待来自被调用方法的响应,否则调用方法不需要等待响应,它可以继续执行其他语句。
在我的情况下如果使用并且在调用方法中不使用await
,则存在巨大的性能差异。即,如果我不使用await
然后它继续执行调用者中的下一个语句而不等待,但它比在调用者中使用await
快得多。
另外,我是否正在使用async&await正确?
码
List<UserViewModel> _ListUser = new List<UserViewModel>();
public XmlElement CreateUpdateUser(Stream input)
{
Main(_ListUser, HttpContext.Current); // using await here makes performance slower and without await it's faster but it returns to the next statement immediately thats the problem.
return FormatResponse("S", "Record(s) created successfully.");
}
public async Task Main(List<UserViewModel> _ListUser, HttpContext current)
{
try
{
WriteToLog("Import Users - Start", 0, DateTime.Now);
UserViewModel _objSiteFileUserSettings = await FillupSiteFileSettings(new UserViewModel());
List<Branch> _branchCollection = await db.Branches.ToListAsync();
List<UserType> _usertypeCollection = await db.UserTypes.ToListAsync();
List<UserStatu> _userstatusCollection = await db.UserStatus.ToListAsync();
List<UserDept> _userdeptCollection = await db.UserDepts.ToListAsync();
List<UserLocation> _userlocationCollection = await db.UserLocations.ToListAsync();
HttpContext.Current = current;
//var tasks = new List<Task>();
foreach (var x in _ListUser)
Update1Record(x, _objSiteFileUserSettings, _branchCollection, _usertypeCollection, _userstatusCollection, _userdeptCollection, _userlocationCollection);
WriteToLog("Import Users - End", 0, DateTime.Now);
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
public string Update1Record(UserViewModel objUser, UserViewModel _objSiteFileUserSettings, List<Branch> _Lbranch, List<UserType> _Lusertype, List<UserStatu> _Luserstatus, List<UserDept> _Luserdept, List<UserLocation> _Luserlocation)
{
objUser.BranchSiteFile = _objSiteFileUserSettings.BranchSiteFile;
objUser.UsrTypeSiteFile = _objSiteFileUserSettings.UsrTypeSiteFile;
objUser.UsrStatSiteFile = _objSiteFileUserSettings.UsrStatSiteFile;
objUser.BranchId = objUser.Branch != null ? CheckBranch(objUser.Branch, _Lbranch) : null;
objUser.UserDeptId = objUser.UserDept != null ? CheckDept(objUser.UserDept, _Luserdept) : null;
objUser.UserLocationId = objUser.UserLocation != null ? CheckLocation(objUser.UserLocation, _Luserlocation) : null;
objUser.UserStatusId = objUser.UserStatus != null ? CheckStatus(objUser.UserStatus, _Luserstatus) : null;
objUser.UserTypeId = objUser.UserType != null ? CheckType(objUser.UserType, _Lusertype) : 0;
objUser._iEmail = _objSiteFileUserSettings._iEmail;
objUser._iSMS = _objSiteFileUserSettings._iSMS;
using (var VibrantDbContext = new VIBRANT())
using (var AuditDb = new VibrantAuditEntities())
using (var VibrantTransaction = VibrantDbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
using (var AuditTransaction = AuditDb.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
try
{
VibrantDbContext.Configuration.AutoDetectChangesEnabled = false;
objUser.RecordTimeStamp = DateTime.Now;
var _ObjUserItem = FillupDateTimeValues(objUser);
ImportToDB(_ObjUserItem, 0, VibrantDbContext, AuditDb);
BuildImportLog(objUser, VibrantDbContext, AuditDb);
VibrantDbContext.SaveChanges();
AuditDb.SaveChanges();
VibrantTransaction.Commit();
AuditTransaction.Commit();
}
catch (Exception ex)
{
VibrantTransaction.Rollback();
AuditTransaction.Rollback();
throw new Exception(ex.ToString());
}
}
return "S";
}
public XmlElement FormatResponse(string Status, string Message)
{
XmlDocument xmlDoc = new XmlDocument();
XmlNode response = xmlDoc.CreateElement("Response");
xmlDoc.AppendChild(response);
XmlNode statusNode = xmlDoc.CreateElement("Status");
statusNode.InnerText = Status;
response.AppendChild(statusNode);
XmlNode MessageNode = xmlDoc.CreateElement("Message");
MessageNode.InnerText = Message;
response.AppendChild(MessageNode);
return xmlDoc.DocumentElement;
}
不,我会说你还没有掌握async / await是什么,以及什么时候使用它们。您应该阅读await / async和Task以完全理解实现,最重要的是使用此结构的原因。关于异步代码的常见误解是它使代码更快。这通常不准确。它实际上使得特定代码稍慢,但它使代码响应更快,并允许您轻松利用服务器的处理能力。
从最简单的意义上讲,您编写或调用的代码通常必须等待某些事情。这可以用于I / O,例如磁盘访问,数据库或其他通信。它还必须等待计算,代码才能完成需要花费大量时间才能完成的工作。如果您有多个这些任务,平均每个5秒,并同步触发它们,第一个任务将在5秒后完成,第二个任务将在10秒后完成,第三个任务将在15秒后完成。
例如:
var result1 = DoSomething1(); //5 seconds.
var result2 = DoSomething2(); //5 seconds.
var result3 = DoSomething3(); //5 seconds.
var total = result1 + result2 + result3; // executes after 15 seconds.
现在,如果我做DoSomething()async
并返回Task<int>
var result1 = DoSomething1();
var result2 = DoSomething2();
var result3 = DoSomething3();
int total = result1 + result2 + result3; // ERROR! result1,2,3 are Taxk<int> representing they are a handle to executing code.
相反,如果我们不关心结果,只需做一个Console.WriteLine("Done");
你几乎可以瞬间达到总线,因为你所做的一切都是启动3x任务。现在,每项任务运行时间都会超过5秒,并返回结果。为了论证,让我们说5.1秒。 (它将小于那个,更像是.01)这种成本代价是针对上下文切换的。您的代码“显示”已即时运行,因为我们已经达到“完成”而工作线程仍在执行我们的任务。 (我们不关心结果吗?处理任何例外?)
所以,现在我们有3x异步方法可以并行运行,我们可以使用await来获得结果:
var result1 = await DoSomething1(); var result2 =等待DoSomething2(); var result3 =等待DoSomething3(); int total = result1 + result2 + result3; //现在可以了
但是,您认为这需要多长时间才能执行?答案是15.3秒。通过使用Await,每个操作必须等待另一个操作完成。
“那么,到底是什么意思呢?!”我相信有些人会问。好吧,要让它们并行运行,你可以像这样写:
var result1 = DoSomething1(); var result2 = DoSomething2(); var result3 = DoSomething3(); int total = await result1 + await result2 + await result3; //这也有效
现在执行时间? 5.1秒
“AHA!所以它更快!”是的,但总的来说只是因为它可以,当为每个操作利用不同的线程是安全的时候,在await之前的任何代码的结果都不依赖于先前异步语句中的任何内容并处理线程安全引用。还有其他注意事项,如同步上下文,异常处理等。还要记住像EF的DbContext这样的对象不是线程安全的,所以调用等待的方法,每个方法引用相同的DbContext而不按顺序等待它们(因为它应该更快并行)将导致丑陋的小问题和错误。
这仅涵盖了async / await的基础知识。您应该从Microsoft和其他来源研究有关其使用和限制的更多阅读,最重要的是何时使用它。它不是一个银弹,不应该默认为可用,因为在大多数情况下我已经看到它使用,它导致性能问题,并且并发错误引用非线程安全代码。它适用于操作可能需要一段时间才能运行的情况,您可以在等待结果之前安全地完成某些代码或启动并行,独立的操作。如果您的同步代码存在性能问题,则99.5%的时间无法通过async / await解决这些问题。在考虑并行操作之前,最好考虑使用现有代码来解释可能的解释。 async / await的主要参数是使代码更具响应性。
一些材料开始:
https://www.codingame.com/playgrounds/4240/your-ultimate-async-await-tutorial-in-c/introduction https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
我相信会有针对SO问题/答案和其他资源的其他建议。
最后说明:不要这样做:
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
抛出新异常会生成一个新的调用堆栈。如果您没有做任何事情来处理异常,只需删除try / catch块。如果你正在做某事并希望异常然后冒泡,请使用throw
:
catch (Exception ex)
{
VibrantTransaction.Rollback();
AuditTransaction.Rollback();
throw;
}
这会保留现有的异常调用堆栈。