برو همزمان تقطیر
آدرس مقاله: https://antonz.org/go-concurrency-distilled/ آدرس نظرات: https://news.ycombinator.com/item?id=49856988 امتیاز: 204 # نظرات: 63
این مینی کتاب مروری کوتاه بر بسیاری از موضوعات همزمان در Go ارائه می دهد. هر موضوع همراه با مثالهای تعاملی است - با تغییر کد و کلیک کردن روی Run، آنها را آزمایش کنید. همچنین یک نسخه PDF با نمونه های ثابت وجود دارد.
این یک تجدید سریع در همزمانی Go است، نه یک راهنمای مبتدی. اگر میخواهید همزمانی را از پایه با تمرینهای عملی یاد بگیرید، کتاب دیگر من - Gist of Go: Concurrency را ببینید.
برنامهها • کانالها • انتخاب • خطوط لوله • زمان • زمینه • گروههای انتظار • مسابقه داده • شرایط مسابقه • Mutexes • Semaphores • سیگنالینگ • یکبار اجرا • استخر شی • اتمی • آزمایش • زمانبندی • تشخیص • افکار نهایی
اساس همزمانی در Go گوروتین ها است – توابع با کلمه کلیدی go شروع می شوند:
زمان اجرا Go این گوروتین ها را به هم می زند و آنها را بین رشته های سیستم عاملی که روی هسته های CPU اجرا می شوند توزیع می کند. در مقایسه با رشته های سیستم عامل، گوروتین ها سبک وزن هستند، بنابراین می توانید صدها یا هزاران مورد از آنها را ایجاد کنید.
گوروتین ها کاملا مستقل هستند. تابع اصلی نیز یک گوروتین است، اما به طور ضمنی با شروع برنامه شروع می شود. وقتی اصلی به پایان می رسد، دیگر گوروتین ها نیز خاموش می شوند.
ما از یک گروه انتظار ( sync.WaitGroup ) استفاده می کنیم تا منتظر بمانیم تا گوروتین ها در مثال بالا تمام شوند. یک گروه انتظار یک پیشخوان در داخل دارد. فراخوانی Add(n) آن را n افزایش می دهد، در حالی که Done() آن را یک کاهش می دهد. Wait() گوروتین فراخوانی را مسدود می کند (در این مورد اصلی) تا زمانی که شمارنده به صفر برسد. به این ترتیب، اصلی منتظر می ماند تا هر دو کارگر قبل از خروج کار را تمام کنند.
WaitGroup.Go به طور خودکار شمارنده گروه انتظار را افزایش می دهد، یک تابع را در یک گوروتین اجرا می کند، و پس از اتمام، شمارنده را کاهش می دهد:
گوروتین ها می توانند مقادیر را از طریق کانال ها به یکدیگر منتقل کنند. یک کانال مانند پنجره ای است که در آن یک گوروتین می تواند چیزی را پرتاب کند و دیگری می تواند آن را بگیرد:
ارسال یک مقدار از طریق یک کانال یک عملیات همزمان است. هنگامی که گوروتین ارسال کننده یک مقدار را به کانال می نویسد ( ch <- val )، آن را مسدود می کند و منتظر می ماند تا کسی آن مقدار را دریافت کند ( <-ch ). فقط پس از آن ادامه می یابد.
برگرداندن یک کانال خروجی از یک تابع و پر کردن آن در یک گوروتین داخلی یک الگوی رایج در Go است. این به تماس گیرنده اجازه می دهد تا مقادیر را از طریق کانال دریافت کند در حالی که تابع مالک کنترل آن را حفظ می کند:
برای اینکه به خوانندگان علامت دهد که همه دادهها ارسال شدهاند، برنامهنویس goroutine کانال را با close() میبندد:
خواننده هنگام خواندن وضعیت کانال را با مقدار دوم ("کاما OK") بررسی می کند:
در حالی که کانال باز است، خواننده مقدار بعدی و وضعیت واقعی را دریافت می کند. اگر کانال بسته باشد، خواننده یک مقدار صفر و یک وضعیت نادرست دریافت می کند.
یک کانال فقط یک بار می تواند بسته شود. بستن مجدد آن یا نوشتن در یک کانال بسته باعث وحشت می شود.
تنها دلیل بستن یک کانال این است که به خوانندگان آن سیگنال ارسال شود که تمام داده ها ارسال شده است. اگر این برای خوانندگان مهم نیست، پس لازم نیست آن را ببندید. وقتی کانالی دیگر استفاده نمیشود، جمعآورنده زباله Go منابع آن را آزاد میکند، چه بسته باشد و چه نباشد.
range به طور خودکار مقدار بعدی را از کانال می خواند و بررسی می کند که آیا بسته است یا خیر. اگر کانال بسته باشد، از حلقه خارج می شود:
محدوده بیش از یک کانال یک مقدار واحد را برمیگرداند، نه یک جفت، برخلاف محدوده بیش از یک برش.
با تنظیم جهت کانال می توانید از اشتباهات تصادفی نوشتن/بستن محافظت کنید. کانال ها می توانند:
شما نمی توانید از یک کانال فقط ارسال بخوانید یا در یک کانال فقط دریافت بنویسید (و نه می توانید آن را ببندید).
کانال ها معمولا برای خواندن و نوشتن مقداردهی اولیه می شوند و در پارامترهای تابع به عنوان جهت دار مشخص می شوند. Go به طور خودکار یک کانال معمولی را به یک کانال جهت دار تبدیل می کند:
کانال های بافر مانند یک صف FIFO با یک بافر با اندازه ثابت برای ذخیره مقادیر کار می کنند.
تا زمانی که بافر فضای خالی داشته باشد، نوشتن در کانال، گوروتین را مسدود نمی کند. به طور مشابه، تا زمانی که بافر حاوی مقادیر باشد، خواندن از کانال، گوروتین را مسدود نمی کند:
بهطور پیشفرض، اگر اندازه بافر را مشخص نکنید، کانال بدون بافر است (اندازه بافر برابر با صفر است).
کانال های بافر با توابع len() و cap() داخلی کار می کنند:
خواندن از یک کانال بسته بافر، مقادیر بافر و وضعیت واقعی را برمیگرداند. هنگامی که تمام مقادیر گرفته شد، یک مقدار صفر و یک وضعیت نادرست، مانند یک کانال معمولی، برمیگرداند:
مانند هر نوع دیگری در Go، کانال ها دارای مقدار صفر هستند که صفر است.
نوشتن به یک کانال صفر یا خواندن از آن، گوروتین را به طور نامحدود مسدود می کند:
دستور select تا حدودی شبیه سوئیچ است، اما به طور خاص برای کانال ها طراحی شده است. در اینجا چیزی است که انجام می دهد:
Select برای مدیریت جریان داده در خطوط لوله استفاده می شود:
خط لوله دنباله ای از عملیات است که در آن هر مرحله داده های ورودی را می گیرد، به روشی خاص پردازش می کند و خروجی می دهد. ورودی و خروجی هر عملیات یک کانال است.
یک گوروتین می تواند به سایر گوروتین ها نشان دهد که کار خود را با استفاده از یک کانال خروجی به پایان رسانده است:
اگر یک گوروتین نیازی به بازگرداندن نتایج نداشته باشد، میتواند با استفاده از یک کانال انجام شده سیگنال تکمیل را نشان دهد:
برای خاتمه دادن زودهنگام یک گوروتین، یک گوروتین تماس گیرنده می تواند از کانال لغو استفاده کند:
سه رویکرد برای رسیدگی به خطا در خطوط لوله همزمان وجود دارد.
علاوه بر مدیریت تاریخ و زمان، بسته زمانی ابزارهایی برای مدیریت عملیات حساس به زمان در برنامههای همزمان ارائه میدهد.
()time.After کانالی را برمیگرداند که در ابتدا خالی است، اما مقداری را پس از دوره زمانی دریافت میکند. برای زمان بندی عملیات مفید است:
withTimeout() منتظر می ماند تا ()fn تکمیل شود، اما به لطف time.After() بیشتر از مدت زمان وقفه منتظر نمی ماند:
تایمر (time.Timer) ساختاری با یک کانال C است که زمان فعلی را هنگام شروع (انقضا) به آن ارسال می کند. تایمرها برای برنامه ریزی اجراهای آینده مفید هستند:
Stop() تایمر را متوقف می کند و اگر هنوز منقضی نشده باشد true را برمی گرداند و در غیر این صورت false را برمی گرداند:
استفاده از تابع زمان ()زمان بسته بندی راحت تر است. برای مدت زمان d منتظر می ماند و سپس تابع f را اجرا می کند:
()time.AfterFunc یک تایمر برمی گرداند که می توانید قبل از شروع اجرا آن را لغو کنید:
اگر یک تایمر در یک حلقه استفاده می شود، بهتر است به جای ایجاد یک نمونه جدید در هر تکرار، یک تایمر واحد ایجاد کنید و آن را بازنشانی کنید:
تیک تیک مانند یک تایمر است، اما تا زمانی که آن را متوقف نکنید به شلیک ادامه می دهد. Ticker ها برای اجرای وظایف دوره ای مفید هستند:
NewTicker(d) یک تیک تیک ایجاد می کند که زمان فعلی را در فاصله d به کانال C ارسال می کند. شما باید در نهایت با Stop() آن را متوقف کنید تا منابع آزاد شود.
اگر خواننده کانال نتواند با تیک تیک همراه شود، تیک تیک تیک ها را رد می کند.
هدف اصلی زمینه لغو عملیات است، چه به صورت دستی و چه با مهلت زمانی/مهلت.
تابع یک متن را می پذیرد و از کانال Done() خود برای گوش دادن برای لغو استفاده می کند:
لغو با مهلت زمانی (context.DeadlineExceeded error):
لغو تا مهلت (context.DeadlineExceeded error):
زمینه لایه بندی شده است. یک شی زمینه تغییر ناپذیر است. برای افزودن ویژگی های جدید به یک زمینه، یک زمینه (فرزند) جدید بر اساس بافت قدیمی (والد) ایجاد می شود. فاصله زمانی کوتاهتر بین زمینههای والد و فرزند همیشه برنده است. زمینه فرزند فقط میتواند مهلت زمانی والدین را کوتاه کند، نه اینکه آن را تمدید کند:
لغو چندگانه بی خطر است. می توانید هر چند بار که بخواهید cancel() را در زمینه فراخوانی کنید. اولین لغو کار خواهد کرد و بقیه نادیده گرفته می شوند.
میتوانید با استفاده از context.WithCancelCause، context.WithTimeoutCause و context.WithDeadlineCause () یک علت لغو سفارشی را مشخص کنید. این علت از طریق context قابل دسترسی است.Cause() :
شما می توانید یک تابع را ثبت کنید تا زمانی که متن لغو می شود با context.AfterFunc() اجرا شود:
Context می تواند با استفاده از context.WithValue() اطلاعات بیشتری در مورد یک فراخوانی ارسال کند، که یک زمینه با مقداری برای یک کلید خاص ایجاد می کند. اما به طور کلی بهتر است از انتقال مقادیر در زمینه خودداری شود. بهتر است به جای آن از پارامترهای صریح یا ساختارهای سفارشی استفاده کنید.
نوع sync.WaitGroup به شما امکان میدهد منتظر بمانید تا یک یا چند گوروتین به پایان برسد:
یک WaitGroup چیزی در مورد گوروتین هایی که مدیریت می کند نمی داند. با یک شمارنده داخلی کار می کند. فراخوانی wg.Add(1) شمارنده را یک برابر افزایش می دهد، در حالی که wg.Done() آن را کاهش می دهد. wg.Wait() گوروتین فراخوانی را تا زمانی که شمارنده به صفر برسد مسدود می کند.
متد Go ترکیبی از Add، شروع یک گوروتین و Done است:
همه روش ها برای استفاده از گوروتین های متعدد بی خطر هستند.
به طور معمول، همه تماسهای افزودن قبل از انتظار انجام میشوند. اما از نظر فنی، هیچ چیزی مانع از انجام برخی از تماسهای افزودن قبل از انتظار و برخی دیگر بعد از آن (از یک برنامه دیگر) نمیشود.
میتوانید از چندین گوروتین با Wait تماس بگیرید. همه آنها تا زمانی که شمارنده گروه به صفر برسد مسدود می شوند.
مسابقه داده زمانی اتفاق میافتد که چندین گوروتین به دادههای مشترک دسترسی داشته باشند و حداقل یکی از آنها آن را تغییر دهد. ما باید از داده ها در برابر این نوع دسترسی همزمان محافظت کنیم.
مسابقه داده همیشه باعث وحشت در زمان اجرا نمی شود. به همین دلیل است که Go ابزار خاصی به نام ردیاب مسابقه ارائه می دهد. می توانید آن را با پرچم مسابقه روشن کنید، که با دستورات تست، اجرا، ساخت و نصب کار می کند.
کانال ها برای خواندن و نوشتن همزمان ایمن هستند و باعث مسابقه داده نمی شوند.
شرایط مسابقه زمانی اتفاق میافتد که ترتیب غیرقابل پیشبینی عملیات از چندین گوروتین منجر به یک وضعیت سیستم نادرست شود:
اگر عملیات فردی همزمان ایمن باشد، آشکارساز مسابقه Go هیچ مشکلی را پیدا نخواهد کرد. به همین دلیل، شرایط مسابقه را نمی گیرد:
شما نمی توانید به طور کامل عدم اطمینان را در یک محیط همزمان از بین ببرید. رویدادها به ترتیب غیرقابل پیشبینی اتفاق میافتند - این دقیقا نحوه عملکرد همزمانی است. با این حال، می توانید از شرایط مسابقه جلوگیری کنید - اغلب با محافظت از یک عملیات ترکیبی با یک mutex:
گاهی اوقات میتوانید بدون استفاده از mutexes با اعمال یک عملیات مقایسه و تنظیم اتمی یا یکی از طعمهای آن از شرایط مسابقه جلوگیری کنید:
نوع sync.Mutex از داده های مشترک و بخش هایی از کد شما در برابر دسترسی همزمان محافظت می کند:
mutex تضمین می کند که تنها یک گوروتین می تواند کد را بین Lock() و Unlock() در یک زمان اجرا کند.
اگر همه گوروتین ها فقط داده ها را می خوانند، نیازی به mutex ندارید.
روش TryLock سعی می کند mutex را قفل کند، درست مانند یک قفل معمولی. اما اگر نتواند، به جای مسدود کردن گوروتین، فورا false را برمیگرداند:
نوع sync.RWMutex بین خواننده و نویسنده تمایز قائل می شود. این دو مجموعه از روش ها را ارائه می دهد:
این یک تنظیم "نویسنده منفرد، چندین خواننده" ایجاد می کند.
هم sync.Mutex و هم sync.RWMutex یک رابط sync.Locker را پیادهسازی میکنند:
با استفاده از Locker به جای یک نوع mutex خاص، می توانید اجزایی بسازید که به اجرای قفل خاصی وابسته نیستند. این به مشتری اجازه می دهد تصمیم بگیرد که از کدام قفل استفاده کند.
می توانید به جای mutex از یک کانال برای محافظت از داده های مشترک استفاده کنید:
سمافور مانند ظرفی با N شکاف در دسترس و دو عملیات است: گرفتن شکاف و رها کردن برای آزاد کردن یک شکاف. در اینجا قوانین سمافور آمده است:
شما می توانید یک سمافور ساده را با یک کانال بافر پیاده سازی کنید، جایی که N اندازه کانال است. برای بدست آوردن سمافور، یک مقدار را به کانال ارسال کنید. برای انتشار آن، یک مقدار از کانال بگیرید:
برای موقعیت های پیچیده تر، از بسته golang.org/x/sync/semaphore استفاده کنید.
یک قرار ملاقات اجازه می دهد تا دو گوروتین منتظر یکدیگر باشند:
شما می توانید یک قرار ملاقات ساده با یک گروه انتظار پیاده سازی کنید:
مانع یک مورد کلی از قرار ملاقات است. این اجازه میدهد تا غروتینها منتظر یکدیگر باشند:
شما می توانید یک مانع ساده را با یک گروه انتظار پیاده سازی کنید:
نوع sync.Cond (متغیر شرطی) به یک گوروتین اجازه میدهد به دیگری سیگنال دهد که آماده است، و به دیگری اجازه میدهد تا برای آن سیگنال منتظر بماند.
یک Cond شامل یک mutex است و دو روش دارد - Wait و Signal.
اگر هنگام فراخوانی سیگنال، چندین مرحله انتظار وجود داشته باشد، تنها یکی از آنها از سر گرفته می شود. اگر هیچ برنامه انتظاری وجود نداشته باشد، سیگنال هیچ کاری انجام نمی دهد.
همچنین می توانید از روش Broadcast استفاده کنید. در حالی که سیگنال تنها یک گوروتین در انتظار Cond.Wait را بیدار میکند، روش Broadcast همه چنین گوروتینهایی را بیدار میکند.
پخش با متغیر شرط محدود است: فقط یک سیگنال ارسال می کند، نه داده واقعی، و فقط یک بار کار می کند. با کانالها، میتوانید یک سیستم انتشار/اشتراک ایجاد کنید که این محدودیتها را نداشته باشد:
نوع sync.Once اطمینان حاصل می کند که تابع داده شده فقط یک بار اجرا می شود. اگر چندین گوروتین به طور همزمان با One.Do تماس بگیرند، تنها یکی این تابع را اجرا می کند، در حالی که بقیه تا بازگشت آن منتظر می مانند:
Once برای مقداردهی اولیه یا پاکسازی یکباره در یک محیط همزمان عالی است.
علاوه بر نوع Once، بسته همگامسازی همچنین شامل سه عملکرد یک بار راحتی است:
نوع sync.Pool به استفاده مجدد از حافظه به جای تخصیص هر بار آن کمک می کند، که بار روی زباله جمع کن را کاهش می دهد:
دریافت یک آیتم از استخر می گیرد. اگر آیتم های موجود نباشد، با استفاده از New یک مورد جدید ایجاد می کند (که باید خودمان تعریف کنیم، زیرا استخر چیزی در مورد آیتم هایی که ایجاد می کند نمی داند). قرار دادن یک آیتم را به استخر برمی گرداند.
یک عملیات بدون همگامسازی تنها زمانی میتواند واقعا اتمی باشد که به یک دستورالعمل پردازشگر تبدیل شود. چنین عملیاتی نیازی به قفل ندارند و هنگام فراخوانی همزمان (حتی عملیات نوشتن) مشکلی ایجاد نمی کنند.
فقط چند اتمی وجود دارد و همه آنها در بسته همگام سازی/اتمی یافت می شوند:
هر نوع اتمی روش های زیر را ارائه می دهد:
انواع عددی همچنین یک متد Add ارائه می کنند که مقدار را به مقدار مشخص شده افزایش می دهد.
همه روشها یا به یک دستورالعمل واحد CPU ترجمه میشوند یا در غیر این صورت تضمین شدهاند که اتمی هستند، بنابراین استفاده از آنها از چندین گوروتین بیخطر است.
ترکیب اتم ها همیشه غیر اتمی است:
یک راه ضد گلوله برای اتمی کردن یک عملیات کامپوزیت و جلوگیری از شرایط مسابقه، استفاده از موتکس است:
گاهی اوقات می توانید از یک نوع اتمی به جای mutex برای خروج زودهنگام استفاده کنید:
اگر برنامه همزمان شما از کانالها یا انواع سفارشی با روشهای همگامسازی مانند Wait استفاده میکند، میتوانید از آنها در آزمایشهای خود استفاده کنید. به این ترتیب، آزمایشهای شما خیلی پیچیدهتر از زمانی که کد همگام بود، نخواهد بود:
اگر در کدی که آزمایش میکنید «دستههای» همگامسازی مناسبی وجود ندارد، میتوانید از بسته همگامسازی استفاده کنید. این دو عملکرد را صادر می کند:
synctest.Test یک حباب ایزوله را اجرا می کند. حباب از یک ساعت تقلبی استفاده میکند و میتوانید بهطور دستی همگامسازی گوروتین را با synctest کنترل کنید. صبر کنید.
همگامسازی. بلوکهای صبر کنید تا زمانی که همه گوروتینهای حباب - به جز موردی که Wait نامیده میشود - تمام شود یا بهطور پایدار مسدود شوند. این به شما امکان می دهد منتظر بمانید تا یک گوروتین خاص تمام شود یا مسدود شود، بنابراین می توانید وضعیت برنامه را بررسی کنید:
ساعت جعلی در synctest.Test فقط در صورتی به جلو حرکت می کند که: ➊ همه گوروتین ها در حباب به طور پایدار مسدود شده باشند. ➋ لحظه ای در آینده وجود دارد که حداقل یک گوروتین از حالت انسداد خارج می شود. و ➌ synctest.Wait اجرا نمی شود. به لطف این، تست های وابسته به زمان فورا اجرا می شوند:
عملیات زیر به طور پایدار یک گوروتین را مسدود می کند:
مسدود کردن mutexes، I/O یا تماسهای سیستمی بادوام تلقی نمیشود و حباب همگامسازی نمیتواند آنها را مدیریت کند.
در سطح سخت افزار، هسته های CPU وظیفه اجرای وظایف موازی را بر عهده دارند.
در سطح سیستم عامل، یک نخ واحد اصلی اجرا است. معمولا تعداد Thread های بسیار بیشتری نسبت به هسته های CPU وجود دارد، بنابراین زمان بندی سیستم عامل تصمیم می گیرد که کدام رشته ها اجرا شوند و کدام ها را متوقف کند.
در سطح زمان اجرا Go، یک گوروتین واحد اصلی اجرا است. زمانبندی زمان اجرا تعداد ثابتی از رشتههای سیستم عامل را اجرا میکند، اغلب یک رشته در هر هسته CPU. میتواند بسیار بیشتر از رشتهها باشد، بنابراین زمانبندی تصمیم میگیرد که کدام گوروتینها را روی رشتههای موجود اجرا کند و کدامها را متوقف کند. زمانبند به جای اینکه برای همیشه در صف منتظر بماند، بین گروتینها جابهجا میشود تا مطمئن شود که هر یک نوبتی برای اجرای یک رشته دارد.
وظیفه زمانبندی گروتین اجرای M goroutine بر روی N رشته سیستم عامل است، جایی که M می تواند بسیار بزرگتر از N باشد. در اینجا یک نسخه بسیار ساده از الگوریتم آن آمده است:
تعداد رشته هایی که کد Go را اجرا می کنند توسط متغیر محیطی GOMAXPROCS یا تابع runtime.GOMAXPROCS کنترل می شود.
گوروتین ساختاری است که با استفاده از حدود 2 کیلوبایت حافظه، بیشتر برای پشته خود، شروع میشود. پشته می تواند در صورت نیاز رشد کند. از آنجایی که گوروتین ها بسیار سبک هستند، می توانید ده ها هزار یا حتی صدها هزار دستگاه از آنها را روی یک دستگاه کوچک اجرا کنید.
برای عیبیابی برنامههای همزمان در تولید، از معیارها، پروفایلسازی و ردیابی استفاده میکنیم.
معیارها نشان میدهند که زمان اجرا Go چگونه کار میکند، مانند مقدار حافظه پشتهای که استفاده میکند یا مدت زمان مکث جمعآوری زباله. هر متریک یک نام و یک مقدار منحصر به فرد دارد که می تواند یک عدد یا یک هیستوگرام باشد.
میتوانید از بسته زمان اجرا/متریک برای دریافت فهرست کامل معیارها یا بررسی مقادیر خاص استفاده کنید:
در عمل، افراد به ندرت این کار را به صورت دستی انجام می دهند. در عوض، تمام معیارها به طور خودکار با استفاده از کتابخانه های Prometheus یا OpenTelemetry صادر می شوند.
نمایه سازی به شما کمک می کند تا بفهمید برنامه دقیقا چه کاری انجام می دهد، از چه منابعی استفاده می کند و در کجای کد این اتفاق می افتد. Go از پروفایل نمونه برداری مناسب برای تولید استفاده می کند.
پرکاربردترین پروفایلها عبارتند از CPU که نشان میدهد هر تابع چقدر از زمان پردازشگر استفاده میکند، و heap که نشان میدهد هر تابع چقدر از حافظه پشته استفاده میکند. نمایههای گوروتین، بلوک و mutex به شناسایی مشکلات مربوط به همزمانی کمک میکنند.
ساده ترین راه برای افزودن پروفایلر به برنامه خود استفاده از بسته net/http/pprof است. برای جمع آوری نمایه ای با نام داده شده، با نقطه پایانی /debug/pprof/{name} تماس بگیرید. برای مشاهده نمایه جمع آوری شده، از ابزار go tool pprof استفاده کنید:
ردیابی انواع خاصی از رویدادها را در حین اجرای برنامه ضبط می کند، عمدتا رویدادهایی که مربوط به همزمانی و حافظه هستند. هنگامی که سرور پروفایل از بسته net/http/pprof در حال اجرا است، نقطه پایانی /debug/pprof/trace را برای جمع آوری یک ردیابی فراخوانی کنید. برای مشاهده نتایج، از ابزار go tool trace استفاده کنید.
می توانید ردیابی خودکار را با یک پنجره کشویی تنظیم کنید که از نظر اندازه یا مدت زمان محدود است. به این "ضبط پرواز" می گویند. این به شما امکان می دهد همیشه یک ردیابی اخیر را در صورت بروز مشکل در دسترس نگه دارید:
ما تعدادی از ابزارهای Go را برای نوشتن برنامه های همزمان پوشش داده ایم:
اگر کتاب را دوست دارید، لطفا آن را به دوستان یا همکاران خود معرفی کنید. اگر علاقه مند هستید، کتاب ها و پروژه های دیگر من را بررسی کنید.
خوشحالم که کتاب رو تموم کردی ممنون و دفعه بعد میبینمت!
متن اصلی (انگلیسی)
Go Concurrency Distilled
Article URL: https://antonz.org/go-concurrency-distilled/ Comments URL: https://news.ycombinator.com/item?id=49856988 Points: 204 # Comments: 63