Огромные различия в производительности, если я не жду в родительском методе C # - PullRequest
0 голосов
/ 26 апреля 2019

Я вижу огромные различия в производительности между использованием и НЕ использованием await от вызывающей стороны.Я чувствую, только разница должна быть по возвращении.т. е. если я использую await, то метод вызывающей стороны должен дождаться ответа от вызываемого метода, прежде чем вернуться к следующему оператору, иначе вызывающему методу не нужно ждать ответа, и он может продолжать выполнять дальнейшие инструкции.

Здесь, в моем случае, существует огромная разница в производительности, если использовать и не использовать await в методе вызывающей стороны.то есть, если я не использую await, то он продолжает выполнять следующую инструкцию в вызывающей стороне без ожидания, но это намного быстрее, чем использование 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;
    }

1 Ответ

0 голосов
/ 26 апреля 2019

Нет, я бы сказал, что вы еще не понимаете, что такое асинхронное / ожидание и когда их использовать. Вы должны прочитать await / async и Task, чтобы полностью понять реализацию и, самое главное, причину использования этой структуры. Распространенное заблуждение об асинхронном коде состоит в том, что он делает код быстрее. Это часто не точно. Это на самом деле делает этот код немного медленнее, , однако , делает код более отзывчивым и позволяет вам легко использовать возможности обработки вашего сервера.

В простейшем смысле код, который вы пишете или вызываете, часто должен чего-то ждать. Это может быть для ввода-вывода, такого как доступ к диску, база данных или другое общение. Также может потребоваться ожидание вычислений, кода, чтобы завершить работу над чем-то, что займет часть времени. Если у вас есть несколько из этих задач, которые в среднем занимают, скажем, 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 секунды. (это будет меньше, больше похоже на 0,01). Это штраф за издержки за переключение контекста. Ваш код «кажется» запустился мгновенно, потому что мы достигли «Готово», в то время как рабочие потоки все еще выполняют наши задачи. (Разве мы не заботимся о результатах? Обработка каких-либо исключений?)

Итак, теперь у нас есть 3x асинхронных метода, доступных для параллельной работы, мы можем использовать await для получения результатов:

var result1 = await DoSomething1 (); var result2 = await DoSomething2 (); var result3 = await DoSomething3 (); int total = результат1 + результат2 + результат3; // Теперь это работает.

Однако, как вы думаете, сколько времени это займет, чтобы выполнить? Ответ будет 15,3 секунды. При использовании Await каждая операция должна ждать завершения другой.

"Ну что, черт возьми, смысл в этом тогда ?!" Я уверен, что некоторые спросили. Ну, чтобы они выполнялись параллельно, вы можете написать это так:

var result1 = DoSomething1 (); var result2 = DoSomething2 (); var result3 = DoSomething3 (); int total = ожидание результата1 + ожидание результата2 + ожидание результата3; // Это тоже работает.

Время выполнения сейчас? 5,1 секунды.

"Ага! Так быстрее!" Да, в целом, но только потому, что это возможно, когда безопасно использовать разные потоки для каждой операции И результаты любого кода ДО того, как ожидание не зависит от чего-либо из предыдущих асинхронных операторов и имеют дело с потоком безопасные ссылки. Есть и другие соображения, такие как контексты синхронизации, обработка исключений и многое другое. Помните также, что такие объекты, как DFContext EF, не поточно-безопасны, поэтому вызов ожидаемых методов, каждый из которых ссылается на один и тот же 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;
        }

Это сохранит существующий стек вызовов для исключения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...