{"id":600,"date":"2021-07-09T21:35:24","date_gmt":"2021-07-10T00:35:24","guid":{"rendered":"https:\/\/millerdantas.com\/blog\/?p=600"},"modified":"2021-07-09T21:45:19","modified_gmt":"2021-07-10T00:45:19","slug":"async-nao-e-paralelismo-e-nao-async-nao-e-mesma-coisa-que-sync-parte-1","status":"publish","type":"post","link":"https:\/\/millerdantas.com\/blog\/2021\/07\/09\/async-nao-e-paralelismo-e-nao-async-nao-e-mesma-coisa-que-sync-parte-1\/","title":{"rendered":"Async n\u00e3o \u00e9 paralelismo e n\u00e3o&#8230; Async n\u00e3o \u00e9 mesma coisa que sync.- Parte 1"},"content":{"rendered":"<h1>Intro<\/h1>\n<p>Calma, leia at\u00e9 o final :sweat_smile: juro que far\u00e1 sentido o t\u00edtulo, bom&#8230; pra mim fez&#8230;..<\/p>\n<p>N\u00e3o sei o quanto voc\u00eas conhecem de Async e a famosa maquina de estado no contexto de C#<\/p>\n<p>particularmente eu j\u00e1 tive v\u00e1rios problemas e cheguei odiar porque sempre tive problema e sempre culpei o compilador&#8230;.<\/p>\n<p>bom, depois de criar vergonha na cara e meter a <em>fu\u00e7a<\/em> em como as coisas funcionam aprendi um pouquinho, o suficiente pra n\u00e3o cair em algumas armadilhas.<\/p>\n<p>tenho alguns Links de refer\u00eancia como meu <a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/github.com\/millerscout\/book-of-reference\/blob\/master\/CSharp.md\" title=\"*book of reference*\"><em>book of reference<\/em><\/a>, tudo que achei \u00fatil quando tive problemas eu acabei adicionando nele pra estudar aos poucos.<\/p>\n<h1>A import\u00e2ncia de saber sobre async e sua m\u00e1quina de estado.<\/h1>\n<p>Recentemente fui convidado pra fazer um Code Review em um c\u00f3digo, particularmente devido o projeto que toco \u00e9 complicado fazer code-review e quando acontece \u00e9 algo bem pontual onde preciso separar na agenda.<\/p>\n<p>e encontrei alguns itens que me chamaram aten\u00e7\u00e3o, foi removido paralelismo, remo\u00e7\u00e3o de async e foi adicionado um tal de GC.Collect().<\/p>\n<p>N\u00e3o me entenda mal, mas se voc\u00ea faz isso, talvez seja importante ler o livro: <a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/www.amazon.com.br\/Pro-NET-Memory-Management-Performance\/dp\/148424026X\" title=\"Pro .Net Memory Management: For Better Code, Performance, and Scalability\">Pro .Net Memory Management: For Better Code, Performance, and Scalability by Kokosa<\/a> ele d\u00e1 um background \u00f3timo sobre com funciona GC e  convenhamos&#8230;  A linguagem j\u00e1 tem mais de 20 anos, com engenheiros super competentes que pensaram durante anos da forma mais efici\u00eante deste processo, vamos ter humildade :sweat_smile:, existem pouqu\u00edssimos casos que voc\u00ea realmente precisa fazer isso, pelo que me recordo em debugging ou fazendo benchmark, com excess\u00e3o desses pontos, voc\u00ea vai apenas piorar a heur\u00edstica e se ele est\u00e1 sendo o problema&#8230; novamente, leia o livro do Kokosa e comece usar o Stack.<\/p>\n<p>bom pulando pra parte que eu queria falar :sleeping:<\/p>\n<h1>Async at last!<\/h1>\n<p>n\u00e3o sei se voc\u00eas tem o costume de ler o que o c\u00f3digo vai gerar ao escrever o c\u00f3digo, n\u00e3o \u00e9 sempre que voc\u00ea precisa disso, na verdade voc\u00ea ignora essa parte porque s\u00e3o poucos cen\u00e1rios que voc\u00ea precisar\u00e1 entender o que acontece por tr\u00e1s do c\u00f3digo gerado ou IL pra determinar uma nova forma de escrever c\u00f3digo<\/p>\n<p>um c\u00f3digo simples  :<\/p>\n<pre><code class=\"language-Csharp line-numbers\">using System;\nusing System.Threading.Tasks;\npublic class TestSubject {\n    public void DoSomething() {\n    }\n    public async Task DoSomethingAsync() {\n    }\n}\n\n<\/code><\/pre>\n<p>e o compilador ir\u00e1 traduzir isso para:<\/p>\n<pre><code class=\"language-csharp line-numbers\">using System;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Security;\nusing System.Security.Permissions;\nusing System.Threading.Tasks;\n\n[assembly: CompilationRelaxations(8)]\n[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]\n[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]\n[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]\n[assembly: AssemblyVersion(\"0.0.0.0\")]\n[module: UnverifiableCode]\npublic class TestSubject\n{\n    [CompilerGenerated]\n    private sealed class &lt;DoSomethingAsync&gt;d__1 : IAsyncStateMachine\n    {\n        public int &lt;&gt;1__state;\n\n        public AsyncTaskMethodBuilder &lt;&gt;t__builder;\n\n        public TestSubject &lt;&gt;4__this;\n\n        private void MoveNext()\n        {\n            int num = &lt;&gt;1__state;\n            try\n            {\n            }\n            catch (Exception exception)\n            {\n                &lt;&gt;1__state = -2;\n                &lt;&gt;t__builder.SetException(exception);\n                return;\n            }\n            &lt;&gt;1__state = -2;\n            &lt;&gt;t__builder.SetResult();\n        }\n\n        void IAsyncStateMachine.MoveNext()\n        {\n            \/\/ILSpy generated this explicit interface implementation from .override directive in MoveNext\n            this.MoveNext();\n        }\n\n        [DebuggerHidden]\n        private void SetStateMachine(IAsyncStateMachine stateMachine)\n        {\n        }\n\n        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)\n        {\n            \/\/ILSpy generated this explicit interface implementation from .override directive in SetStateMachine\n            this.SetStateMachine(stateMachine);\n        }\n    }\n\n    public void DoSomething()\n    {\n    }\n\n    [AsyncStateMachine(typeof(&lt;DoSomethingAsync&gt;d__1))]\n    [DebuggerStepThrough]\n    public Task DoSomethingAsync()\n    {\n        &lt;DoSomethingAsync&gt;d__1 stateMachine = new &lt;DoSomethingAsync&gt;d__1();\n        stateMachine.&lt;&gt;t__builder = AsyncTaskMethodBuilder.Create();\n        stateMachine.&lt;&gt;4__this = this;\n        stateMachine.&lt;&gt;1__state = -1;\n        stateMachine.&lt;&gt;t__builder.Start(ref stateMachine);\n        return stateMachine.&lt;&gt;t__builder.Task;\n    }\n}\n\n<\/code><\/pre>\n<p>\u00e9 poss\u00edvel perceber que toda vez que voc\u00ea deixa uma assinatura de &#8220;async&#8221; no m\u00e9todo ele gera uma m\u00e1quina de estado, \u00e9 at\u00e9 um dos motivos que eu tento n\u00e3o criar <em>nested async<\/em>, afinal invari\u00e1velmente ser\u00e1 alocado em mem\u00f3ria, ent\u00e3o em cen\u00e1rios cr\u00edticos eu penso bem&#8230; Continuando&#8230;.<\/p>\n<p>Precisava dar esse contexto caso algu\u00e9m n\u00e3o tivesse ainda a oportunidade de  saber o que acontece por baixo dos panos, ent\u00e3o foi um resumo pra continuar<\/p>\n<p>em minha mem\u00f3ria do c\u00f3digo de algum tempo atr\u00e1s era o seguinte:<\/p>\n<pre><code class=\"language-csharp line-numbers\">public async Task SomeMainTask(){\n\n    var id = 0;\n    var processoUm = Task.Run(()=&gt;{ id = DoSomething();});\n    var processoDois = DoOtherThing();\n\n    await processoUm;\n\n    (... outros processos sincronos e ass\u00edncronos)\n\n    var processoDois = DoOtherThing(id);\n\n    Task.WaitAll(\n        processoDois,\n        (...)\n    );\n}\n\npublic async Task&lt;id&gt; DoSomething(){\n    (... alguma chamada pro banco)\n    \/\/\/c\u00f3digo apenas pra contexto\n    return Task.FromResult(id);\n\n}\n\n<\/code><\/pre>\n<p>e quando eu li o c\u00f3digo:<\/p>\n<pre><code class=\"language-csharp line-numbers\">public void  SomeMainTask(){\n\n    var id = 0;\n    var processoUm = DoSomething();\n    var processoDois = DoOtherThing();\n\n    (... outros processos sincronos e ass\u00edncronos)\n\n    var processoDois = DoOtherThing(id);    \n}\n\npublic id DoSomething(){\n    (... alguma chamada pro banco)\n    \/\/\/c\u00f3digo apenas pra contexto\n    return id;\n\n}\n\n<\/code><\/pre>\n<p>eu fiquei meio intrigado, eu queria entender a motiva\u00e7\u00e3o&#8230; particularmente eu fa\u00e7o tudo sincrono mesmo e eventualmente quando estou fazendo meu pr\u00f3prio code-review eu penso se faz ou n\u00e3o sentido colocar um async.<\/p>\n<p>quando questionei, a resposta foi que async e sincrono seriam as mesmas coisas no final das contas  ou seja :<\/p>\n<p>isso<\/p>\n<pre><code class=\"language-csharp line-numbers\">var id = DoSomething();\n\npublic int DoSomething(){\n    (...chamada pro banco)\n    return someid;\n}\n<\/code><\/pre>\n<p>\u00e9 igual isso:<\/p>\n<pre><code class=\"language-csharp line-numbers\">var id = await DoSomething();\n\npublic Task&lt;int&gt; DoSomething(){\n    (...chamada pro banco)\n    using(var someconn = new SqlConnection(connectionstring)){\n        return someconn.FirstOrDefaultAsync&lt;int&gt;(\"select 1\");   \n    }   \n}\n<\/code><\/pre>\n<p>ser\u00e1?<\/p>\n<p>tentei explicar que n\u00e3o era&#8230; afinal na pr\u00f3pria <a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/concepts\/async\/#dont-block-await-instead\" title=\"documenta\u00e7\u00e3o da microsoft\">documenta\u00e7\u00e3o da microsoft<\/a> (inclusive&#8230; que maravilha de doc ) explica exatamente o que eu disse. Apesar do meu esfor\u00e7o de mostrar que ele vai bloquear uma thread at\u00e9 ter resposta&#8230;<\/p>\n<p>achei mais simples criar um c\u00f3digo muito simples pra apresentar o problema:<\/p>\n<p>Apenas pra dar contexto, eu sou um usu\u00e1rio ass\u00edduo do LinqPad, logo quando estiver escrito &#8220;someText&#8221;.Dump(); no c\u00f3digo abaixo, entenda como Console.WriteLine(&#8220;sometext&#8221;);<\/p>\n<pre><code class=\"language-csharp line-numbers\">async System.Threading.Tasks.Task Main()\n{\n    int nWorkers; \n    int nCompletions; \n\n    \/\/\/aqui estou for\u00e7ando que apenas exista uma \u00fanica Thread no meu sistema pra apresentar um cen\u00e1rio que d\u00e1 pra observar o comportamento\n    ThreadPool.SetMinThreads(1, 1).Dump();\n    ThreadPool.SetMaxThreads(1, 1).Dump();\n\n    \/\/\/ apenas me assegurando que \u00e9 apenas uma thread..... vai que... \n    ThreadPool.GetMaxThreads(out nWorkers, out nCompletions);\n        <span class=\"katex math inline\">\"workers: {nWorkers}\".Dump();<\/span>\"nCompletions: {nWorkers}\".Dump();\n\n    \/\/\/estou deixando uma thread executando sem parar, com um intervalo de 2 segundos, como se fosse um heart-beat.\n    System.Threading.Tasks.Task.Run(async ()=&gt;{\n                while(true){\n                    <span class=\"katex math inline\">\"Heartbeat\".Dump();\n                     await System.Threading.Tasks.Task.Delay(2000);\n                }\n    });\n\n\n    for(var i = 0; i&lt;=4; i++){\n        \/\/\/ aqui estou criando algumas Tasks ass\u00edncronas para executar alguma a\u00e7\u00e3o representando um insert no banco.\n        await System.Threading.Tasks.Task.Run(async ()=&gt;{\n                await System.Threading.Tasks.Task.Delay(5000);<\/span>\"{Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString(\"HH:MM:ss\")}\".Dump();\n        });\n    }\n\n    \"-----------------------Async Done-----------------------\".Dump();\n    for(var i = 0; i&lt;=4; i++){\n        \/\/\/ aqui estou criando as mesmas Tasks mas desta vez estou for\u00e7ando a execu\u00e7\u00e3o s\u00edncrona para executar a mesma a\u00e7\u00e3o no banco.\n            System.Threading.Tasks.Task.Run(()=&gt;{\n                System.Threading.Tasks.Task.Delay(5000).Wait();\n                $\"{i} - {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString(\"HH:MM:ss\")}\".Dump();\n            });\n    }\n}\n<\/code><\/pre>\n<p>O que voc\u00ea acha que aconteceu?<\/p>\n<p>eu tive que gravar um v\u00eddeo&#8230;:sweat_smile:<\/p>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/sGjQrwZgErw\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<p>como esperado a thread que est\u00e1 respos\u00e1vel pelo heart-beat continua a execu\u00e7\u00e3o, \u00e9 liberada e ent\u00e3o a task Async de &#8220;persist\u00eancia no banco&#8221; consegue trabalhar, termina e assim o fluxo continua..<\/p>\n<p>agora quando chega no s\u00edncrono&#8230; nenhum funciona, infelizmente n\u00e3o consegui compreender o motivo, a thread acabou ficando em lock no Task.Delay, mas isso eu n\u00e3o vou tentar entender hoje.<\/p>\n<p>mas espero que esse post traga a luz para algu\u00e9m que assim como eu tamb\u00e9m j\u00e1 sofreu com async.<\/p>\n<p>p.s. eu sei que acontecer um thread starvation em alguns cen\u00e1rios \u00e9 dif\u00edcil dependendo do seu cen\u00e1rio, mas no cen\u00e1rio que eu verifiquei isso pode ser a difer\u00ean\u00e7a entre aumentar a capacidade do servidor pra dar vaz\u00e3o no processamento, sendo que o custo pode ser evitado com um uso intelig\u00eante dos recursos.<\/p>\n<p>p.s\u00b2 Pegadinha! eu n\u00e3o falei sobre paralelismo ainda :laughing:, mas o que eu falaria n\u00e3o escapa da gloriosa doc da <a class=\"wp-editor-md-post-content-link\" href=\"http:\/\/https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/concepts\/async\/#start-tasks-concurrently\" title=\"m\u00e3ecrosoft\">m\u00e3ecrosoft<\/a>:<\/p>\n<p>e depois continuo :p<\/p>\n<p>Esteja Curioso!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Intro Calma, leia at\u00e9 o final :sweat_smile: juro que far\u00e1 sentido o t\u00edtulo, bom&#8230; pra mim fez&#8230;.. N\u00e3o sei o quanto voc\u00eas conhecem de Async e a famosa maquina de estado no contexto de C# particularmente eu j\u00e1 tive v\u00e1rios problemas e cheguei odiar porque sempre tive problema e sempre culpei o compilador&#8230;. bom, depois &hellip; <a href=\"https:\/\/millerdantas.com\/blog\/2021\/07\/09\/async-nao-e-paralelismo-e-nao-async-nao-e-mesma-coisa-que-sync-parte-1\/\" class=\"more-link\">Continue lendo <span class=\"screen-reader-text\">Async n\u00e3o \u00e9 paralelismo e n\u00e3o&#8230; Async n\u00e3o \u00e9 mesma coisa que sync.- Parte 1<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-600","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/posts\/600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/comments?post=600"}],"version-history":[{"count":5,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/posts\/600\/revisions"}],"predecessor-version":[{"id":605,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/posts\/600\/revisions\/605"}],"wp:attachment":[{"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/media?parent=600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/categories?post=600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/millerdantas.com\/blog\/wp-json\/wp\/v2\/tags?post=600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}