របៀបដែលយើងបានរចនា Swarm, ប្រព័ន្ធសាកល្បងផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុក របៀបដែលយើងបានរចនា Swarm, ប្រព័ន្ធសាកល្បងផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុកផ្ទុក ឥឡូវនេះវាគឺជាការងាយស្រួលណាស់ដើម្បីរចនាឡើងនូវផលិតកម្មកម្មវិធីដែលមិនមាន ការធ្វើតេស្តកម្រិតនៃការធ្វើតេស្ត។ ការធ្វើតេស្តឧបករណ៍គឺជាវិធីសាស្រ្តធម្មតានៅក្នុងការកាត់បន្ថយកំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់កំណត់ក មួយចំនួន ខ្ញុំជា Andrey Rakhubov ដែលជាអ្នកវិស្វករកម្មវិធីសំខាន់នៅ MY.GAMES! នៅក្នុងប្រកាសនេះខ្ញុំនឹងផ្លាស់ប្តូរទិន្នន័យអំពីការផ្លាស់ប្តូរដែលប្រើនៅក្នុងសាកលវិទ្យាល័យរបស់យើងនិងវិធីសាស្រ្តរបស់យើងក្នុងការធ្វើតេស្តការផ្ទុកនៃការធ្វើតេស្ត meta-game នៅ War Robots: Frontiers ។ សៀវភៅសៀវភៅសៀវភៅសៀវភៅសៀវភៅសៀវភៅសៀវភៅសៀវភៅ ដោយគ្មានការចូលទៅក្នុងលក្ខណៈពិសេសណាមួយដែលមិនចាំបាច់, ការបង្វិលរបស់យើងរួមបញ្ចូលគ្នាជាប្រភេទនៃសេវាកម្មអាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាសអាភាស។ ទោះជាយ៉ាងណាក៏ដោយមានបញ្ហានៃការផ្សេងទៀតដែលអាចមានវិធីសាស្រ្តនេះបានបង្ហាញថាវាគឺជាការល្អណាស់ក្នុងការដោះស្រាយមួយនៃបញ្ហានេះដែលមានភាពងាយស្រួលបំផុតនៅក្នុងការអភិវឌ្ឍន៍ហ្គេម: ល្បឿន iteration ។ អ្នកអាចយល់ថាក្នុងរយៈពេលដំបូងនៃការអភិវឌ្ឍន៍ Rust-lang មានការហ្គេមថាឯកសារនឹងផ្លាស់ប្តូរដូចដែលអ្នកបានអាន។ ដូច្នេះយើងមានន័យដូចគ្នាអំពីឯកសារ GDD (Game Design Document) ។ នេះ អ្នកជំនាញបានជួយក្នុងវិធីដែលពួកគេគឺជាការងាយស្រួលក្នុងការអនុវត្តនិងការផ្លាស់ប្តូរនៅក្នុងជំហានដំបូងនៃការរចនាសម្ព័ន្ធលក្ខណៈពិសេសនិងពួកគេគឺជាការងាយស្រួលក្នុងការ refactor ជាសេវាកម្មដោយផ្ទាល់ប្រសិនបើតម្រូវការកើតឡើងបន្ទាប់មក។ ការធ្វើតេស្តផ្ទុកនិងការរួមបញ្ចូលនេះមានភាពងាយស្រួលប្រសិនបើអ្នកមិនមានការបាត់បង់សេវាកម្មយ៉ាងច្បាស់លាស់ជាមួយនឹង API ដែលមានគុណភាពខ្ពស់និងមានតម្រូវការខ្ពស់។ ដូច្នេះអ្នកមិនមានការបាត់បង់សេវាកម្មដែលមានតម្រូវការខុសគ្នានិងអាចត្រួតពិនិត្យបានយ៉ាងហោចណាស់។ ការធ្វើតេស្ត ដូចដែលអ្នកអាចជឿទុកចិត្ត, ការធ្វើតេស្តឧបករណ៍សម្រាប់កម្មវិធីនិងសេវាកម្ម (និងការធ្វើតេស្តផ្ទុក / ការបណ្តុះបណ្តាលសម្រាប់សេវាកម្ម) មិនមានប្រភេទផ្សេងគ្នានៃអ្វីដែលអ្នកនឹងរកឃើញនៅក្នុងប្រភេទផ្សេងទៀតនៃកម្មវិធី។ ការផ្សេងគ្នានៅក្នុង: ការធ្វើតេស្តផ្ទុកនិងការរួមបញ្ចូល ការធ្វើតេស្ត end-to-end និង load backend យើងនឹងមិនមើលឃើញការធ្វើតេស្តនៃការធ្វើតេស្តនៅក្នុងពិសេសនៅទីនេះដោយសារតែវាមិនមានលក្ខណៈពិសេសសម្រាប់ GameDev ទេប៉ុន្តែទាក់ទងទៅនឹងម៉ូដែលអ្នកធ្វើតេស្តរបស់ខ្លួន។ គោលបំណងដែលធម្មតានៅក្នុងការធ្វើតេស្តនេះគឺមានក្លឹបមួយគត់មួយដែលអាចត្រូវបានបណ្តុះបណ្តាលនៅក្នុងការចែកចាយក្នុងការធ្វើតេស្តតែមួយហើយដែលអាចបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះបណ្តុះប ដូច្នេះអ្វីដែលបានចាប់ផ្តើមបានចាប់ផ្តើមមានភាពងាយស្រួលបំផុតនៅពេលដែលយើងបានមកដល់ការផ្ទុកឬការធ្វើតេស្ត end-to-end - និងទីនេះគឺជាការចាប់ផ្តើមនៃប្រវត្តិសាស្រ្តរបស់យើង។ ដូច្នេះនៅក្នុងការរបស់យើងកម្មវិធីអតិថិជនគឺហ្គេមដោយខ្លួនឯង។ ហ្គេមប្រើម៉ាស៊ីន Unreal, ដូច្នេះកូដត្រូវបានសរសេរនៅក្នុង C++, និងនៅលើបណ្តាញយើងប្រើ C# ។ អ្នកលេងផ្លាស់ប្តូរជាមួយអេឡិចត្រូនិអេឡិចត្រូនិនៅក្នុងហ្គេម, ការផលិតតម្រូវការទៅ backend (ឬតម្រូវការត្រូវបានធ្វើដោយប្រាកដ) ។ នៅពេលនេះអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជនអតិថិជន ចំណុចបន្ទាប់នេះគឺថាយើងប្រើប្រព័ន្ធប្រតិបត្តិការទំនាក់ទំនងផ្ទាល់ខ្លួនរវាងអតិថិជននិង backend ។ ការផ្លាស់ប្តូរនេះពិតជាគួរឱ្យមានអត្ថបទផ្ទាល់ខ្លួនទេប៉ុន្តែខ្ញុំនឹងបង្ហាញនូវគំនិតសំខាន់ៗនេះ: ការទំនាក់ទំនងធ្វើឡើងតាមរយៈការតភ្ជាប់ WebSocket វាគឺជាគំនិតជាលើកដំបូង; យើងប្រើ Protobuf ដើម្បីកំណត់រចនាសម្ព័ន្ធព័ត៌មាននិងសេវាកម្ម ការប្រកាស WebSocket គឺជាការប្រកាស Protobuf ដែលត្រូវបានវេចខ្ចប់ទៅក្នុងកញ្ចក់ដែលមានទិន្នន័យប្លាស្ទិចដែលធ្វើឱ្យប្រសើរឡើងនូវទិន្នន័យ gRPC ដែលត្រូវការដូចជា URL និង headers ។ ដូច្នេះឧបករណ៍ណាមួយដែលមិនអនុញ្ញាតឱ្យកំណត់ប្រព័ន្ធប្រតិបត្តិការផ្ទាល់ខ្លួនគឺមិនសមរម្យសម្រាប់ការងារនេះ។ ឧបករណ៍ផ្ទុកដើម្បីធ្វើតេស្តពួកគេទាំងអស់ នៅពេលនេះយើងចង់មានឧបករណ៍តែមួយដែលអាចសរសេរការធ្វើតេស្តផ្ទុក REST / gRPC និងការធ្វើតេស្ត end-to-end ជាមួយនឹងប្រព័ន្ធផ្សព្វផ្សាយផ្ទាល់ខ្លួនរបស់យើង។ បន្ទាប់ពីគិតអំពីតម្រូវការទាំងអស់ដែលត្រូវបានពិភាក្សានៅក្នុងការធ្វើតេស្តនិងការពិភាក្សាដំបូងមួយចំនួនយើងត្រូវបានបាត់បង់ជាមួយគម្រោងទាំងនេះ: k6 Locust NBomber K6 ។ សត្វ ហ្វេសប៊ុក ពួកគេទាំងអស់មានគោលបំណងនិងគោលបំណងរបស់ពួកគេ, ប៉ុន្តែមានច្រើននៃអ្វីដែលពួកគេមិនអាចដោះស្រាយដោយគ្មានការផ្លាស់ប្តូរធំទូលំទូលាយ (ម្តងទៀតផ្ទាល់) ។ ជាលើកដំបូង, មានតម្រូវការដែលទាក់ទងនឹងការទំនាក់ទំនងរវាង bot សម្រាប់គោលបំណងនៃការសន្សំ, ដូចដែលបាននិយាយមុន។ ទីពីរ, កុំព្យូទ័រមានប្រសិទ្ធភាពយ៉ាងខ្លាំងនិងស្ថានភាពរបស់ពួកគេអាចផ្លាស់ប្តូរដោយគ្មានការដោះស្រាយដោយពិសេសពីប្រសិនបើមានប្រសិនបើមានការធ្វើតេស្ត; នេះធ្វើឱ្យមានការត្រួតពិនិត្យបន្ថែមទៀតនិងការតម្រូវការដើម្បីដោះស្រាយតាមរយៈការដោះស្រាយជាច្រើននៅក្នុងកូដរបស់អ្នក។ ទោះជាយ៉ាងណាក៏ដោយឧបករណ៍ទាំងនេះត្រូវបានផ្តោតលើការធ្វើតេស្តប្រសិទ្ធភាពយ៉ាងតឹងរឹងដោយផ្តល់នូវលក្ខណៈពិសេសជាច្រើនសម្រាប់ការកើនឡើងរំខានឬបញ្ជាក់អំឡុងពេលវេលាប៉ុន្តែមិនមានសមត្ថភាពដើម្បីបង្កើតប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនប ឥឡូវនេះគឺជាពេលដើម្បីទទួលយកការពិតដែលយើងពិតជាត្រូវការឧបករណ៍ដែលមានជំនាញ។ ដូច្នេះឧបករណ៍របស់យើង - ខ្ញុំបានក្លាយ សត្វ សត្វ នៅកម្រិតខ្ពស់សកម្មភាពនៃ Swarm គឺដើម្បីចាប់ផ្តើមជាច្រើននៃអ្នកចូលរួមដែលនឹងផលិតការផ្ទុកនៅលើសេវាកម្ម។ អ្នកចូលរួមទាំងនេះត្រូវបានគេហៅថា bots ហើយពួកគេអាចរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការ លក្ខណៈពិសេសនេះគឺដូច្នេះនៅទីនេះគឺជាសៀវភៅនៃតម្រូវការសម្រាប់ឧបករណ៍នេះ: ការឆ្លើយតបទៅនឹងការបច្ចុប្បន្នភាពរដ្ឋបាលគួរតែមានភាពងាយស្រួល ការប្រកួតប្រជែងមិនគួរតែជាបញ្ហា គោលបំណងគោលបំណងគោលបំណងគោលបំណង ការទំនាក់ទំនង bot-to-bot នឹងមានភាពងាយស្រួល លក្ខណៈសម្បត្តិផ្សេងគ្នានៅក្នុងការបង្កើតកម្រិតអតិបរមា ឧបករណ៍នេះគួរតែមានភាពងាយស្រួលនិងអាចបង្កើតសម្ពាធល្អនៅលើ backend របស់ខ្លួន។ ក្នុងនាមជាប្រាក់រង្វាន់ខ្ញុំបានបន្ថែមចំនួនបន្ថែមទៀត: ការធ្វើតេស្តកម្រិតខ្ពស់និងការធ្វើតេស្ត end-to-end នឹងអាចធ្វើបាន ឧបករណ៍នេះគួរតែជាការផ្លាស់ប្តូរ - agnostic; យើងគួរតែអាចផ្លាស់ប្តូរវាទៅនឹងការផ្លាស់ប្តូរផ្សេងទៀតដែលអាចធ្វើបានប្រសិនបើមានតម្រូវការ ឧបករណ៍នេះមានប្រភេទកូដ imperative ដូចជាខ្ញុំផ្ទាល់ខ្លួនមានអារម្មណ៍យ៉ាងខ្លាំងថាប្រភេទ declarative គឺមិនសមរម្យសម្រាប់ប្រវត្តិសាស្រ្តមានតម្រូវការដែលមានតម្រូវការ កុំព្យូទ័រគួរតែអាចមានតែមួយគត់ពីឧបករណ៍ធ្វើតេស្តដែលមានន័យថាមិនគួរតែមានការផ្លាស់ប្តូររឹងទេ។ គោលដៅរបស់យើង សូមចង់រៀបចំនៃកូដដែលយើងចង់សរសេរ។ យើងនឹងគិតថា bot គឺជាបារាំងហើយវាមិនអាចធ្វើអ្វីបានដោយខ្លួនឯងប៉ុន្តែវាអាចរក្សាទុកការប្រែប្រែប្រែប៉ុណ្ណោះប៉ុន្តែ Scenario គឺជាបារាំងដែលបាត់បង់បារាំង។ public class ExampleScenario : ScenarioBase { /* ... */ public override async Task Run(ISwarmAgent swarm) { // spawn bots and connect to backend var leader = SpawnClient(); var follower = SpawnClient(); await leader.Login(); await follower.Login(); // expect incoming InviteAddedEvent var followerWaitingForInvite = follower.Group.Subscription .ListenOnceUntil(GroupInviteAdded) .ThrowIfTimeout(); // leader sends invite and followers waits for it await leader.Group.SendGroupInvite(follower.PlayerId); await followerWaitingForInvite; Assert.That(follower.Group.State.IncomingInvites.Count, Is.EqualTo(1)); var invite = follower.Group.State.IncomingInvites[0]; // now vice versa, the leader waits for an event... var leaderWaitingForAccept = leader.Group.Subscription .ListenOnceUntil(InviteAcceptedBy(follower.PlayerId)) .ThrowIfTimeout(); // ... and follower accept invite, thus producing the event await follower.Group.AcceptGroupInvite(invite.Id); await leaderWaitingForAccept; Assert.That(follower.Group.State.GroupId, Is.EqualTo(leader.Group.State.GroupId)); PlayerId[] expectedPlayers = [leader.PlayerId, follower.PlayerId]; Assert.That(leader.Group.State.Players, Is.EquivalentTo(expectedPlayers)); Assert.That(follower.Group.State.Players, Is.EquivalentTo(expectedPlayers)); } } ដំបូង ការផ្លាស់ប្តូរបច្ចុប្បន្នភាពជាច្រើនទៅដល់អតិថិជនដែលអាចកើតឡើងយ៉ាងឆាប់រហ័ស; នៅក្នុងឧទាហរណ៍ខាងលើ, ការប្រៀបធៀបនេះគឺ ការធ្វើតេស្តនេះគួរតែមានសមត្ថភាពដើម្បីធ្វើតេស្តទាំងពីរទៅនឹងការធ្វើតេស្តទាំងនេះដោយផ្ទាល់ខ្លួននិងផ្តល់នូវឱកាសដើម្បីមើលពួកគេពីកូដខាងក្រៅ។ GroupInviteAddedEvent public Task SubscribeToGroupState() { Subscription.Listen(OnGroupStateUpdate); Subscription.Start(api.GroupServiceClient.SubscribeToGroupStateUpdates, new SubscribeToGroupStateUpdatesRequest()); return Task.CompletedTask; } ទោះបីជាកូដនេះគឺជាភាពងាយស្រួលណាស់ (និងដូចអ្នកអាចគិតថា) ប្រព័ន្ធ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ OnGroupStateUpdate ដោយខ្លួនឯង a StreamSubscription IObservable<ObservedEvent<TStreamMessage>> និងវាផ្តល់នូវការបន្ថែមដែលមានប្រសិទ្ធិភាព, មានប្រវែងជីវិតដែលមានគោលបំណង, និងត្រូវបានបណ្តុះបណ្តាលដោយគំនិត។ អត្ថប្រយោជន៍ផ្សេងទៀត: វាគឺជាការតម្រូវការការសន្សំដោយគ្មានការសន្សំដោយគ្មានការសរសេរឬការផ្លាស់ប្តូរស្ថានភាពដោយគ្មានការផ្លាស់ប្តូរកន្លែងដែលអ្នកដំណើរការត្រូវបានអនុវត្ត។ case GroupUpdateEventType.GroupInviteAddedEvent: State.IncomingInvites.Add(ev.GroupInviteAddedEvent.GroupId, ev.GroupInviteAddedEvent.Invite); break; ការប្រកួតប្រជែងមិនគួរតែជាបញ្ហា គំនិតនេះគឺជាការងាយស្រួល: ទោះជាយ៉ាងណាក៏ដោយមិនចាំបាច់អនុវត្តកូដដែលចូលរួមក្នុងប្រព័ន្ធប្រតិបត្តិការឬ bot ដែលត្រូវបានបង្កើតឡើងនៅក្នុងប្រព័ន្ធប្រតិបត្តិការនេះទេ។ លើសពីនេះទៀត, កូដខាងក្នុងសម្រាប់ bot / module នឹងមិនត្រូវបានអនុវត្តនៅពេលដែលផ្នែកផ្សេងទៀតនៃ bot ។ នេះគឺដូចជាការសរសេរ / សរសេរកូដដែលជាកូដសរសេរ (ការចូលរួម) និងកូដ bot ដែលជាការសរសេរ (ការចូលរួមតែមួយគត់) ហើយនៅពេលដែលគោលបំណងរបស់យើងអាចត្រូវបានទទួលបានដោយប្រើប្រភេទនៃកូដនេះមានវិធីល្អបំផុត។ ការរៀបចំនៃការធ្វើតេស្តនិងគំនិត synchronization ការប្រៀបធៀបទាំងពីរដែលបានប្រៀបធៀបនៅក្នុងការប្រៀបធៀបនេះគឺជាផ្នែកដែលមានប្រសិទ្ធិភាពខ្លាំងណាស់នៃកូដ async នៅក្នុង C# ។ ប្រសិនបើអ្នកបានអភិវឌ្ឍកម្មវិធី WPF អ្នកអាចដឹងថាតើវាជាកម្មវិធីដែលមានប្រសិទ្ធិភាព។ វាគឺជាការគ្រប់គ្រងសម្រាប់ការផ្លាស់ប្តូរការទូរស័ព្ទ async របស់អ្នកទៅលើ UI thread ។ DispatcherSynchronizationContext នៅក្នុងប្រវត្តិសាស្រ្តរបស់យើងយើងមិនចង់ចាប់អារម្មណ៍អំពីការរៀបចំនៃភ្នាល់ទេហើយជាផ្នែកមួយដែលយើងចង់ចាប់អារម្មណ៍អំពីដំណាក់កាលនៃការងារនៅក្នុងប្រវត្តិសាស្រ្តមួយ។ មុនពេលធ្វើការដើម្បីសរសេរកូដកម្រិតទាបសូមមើលកម្រិតមួយដែលមិនត្រូវបានគេស្គាល់យ៉ាងទូលំទូលំទូលាយ ពី the : ConcurrentExclusiveSchedulerPair ដំណឹង ដំណឹង អនុញ្ញាតឱ្យអ្នករៀបរៀបរៀបរៀបរៀបការដើម្បីដោះស្រាយកម្មវិធីក្នុងពេលដែលការធានាថានៅពេលដែលកម្មវិធីផ្សេងគ្នានេះអាចដោះស្រាយកម្មវិធីផ្សេងគ្នានិងកម្មវិធីផ្សេងគ្នានេះមិនអាចដោះស្រាយកម្មវិធី។ អនុញ្ញាតឱ្យអ្នករៀបរៀបរៀបរៀបរៀបការដើម្បីដោះស្រាយកម្មវិធីក្នុងពេលដែលការធានាថានៅពេលដែលកម្មវិធីផ្សេងគ្នានេះអាចដោះស្រាយកម្មវិធីផ្សេងគ្នានិងកម្មវិធីផ្សេងគ្នានេះមិនអាចដោះស្រាយកម្មវិធី។ នេះជាអ្វីដែលយើងចង់មើលឃើញ! ឥឡូវនេះយើងត្រូវការធ្វើឱ្យប្រាកដថាកូដទាំងអស់ក្នុងប្រព័ន្ធប្រតិបត្តិការនេះត្រូវបានដំណើរការនៅលើ នៅពេលដែលកូដរបស់ bot បានអនុវត្តនៅលើ . ConcurrentScheduler ExclusiveScheduler តើធ្វើដូចម្តេចដើម្បីកំណត់រចនាសម្ព័ន្ធសម្រាប់ការធ្វើតេស្តមួយ? ប្រភេទមួយគឺដើម្បីផ្ញើវាជាទំហំដោយពិសេសដែលធ្វើឡើងនៅពេលដែលប្រព័ន្ធប្រតិបត្តិការចាប់ផ្តើម: public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch) { return Task.Factory.StartNew( () => { /* <some observability and context preparations> */ return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap(); } នេះ គោលបំណងនេះបើយោងតាមគោលបំណង និង ប្រព័ន្ធ ប្រតិបត្តិការ iOS ប្រព័ន្ធ ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ប្រតិបត្តិការ iOS ជាផ្នែកមួយនៃការប្រកួតប្រជែង ) ។ RunScenarioInstance SetUp Run ScenarioScheduler ConcurrentExclusiveSchedulerPair ឥឡូវនេះ, នៅពេលដែលយើងមាននៅក្នុងកូដសិល្បៈនិងយើងធ្វើការនេះ ... public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch) { return Task.Factory.StartNew( () => { /* <some observability and context preparations> */ return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap(); } ...ម៉ាស៊ីន async បានធ្វើការងាររបស់ខ្លួនសម្រាប់យើងដោយការរក្សាទុកកំណត់សម្រាប់ការងាររបស់យើង។ ឥឡូវនេះ វាត្រូវបានធ្វើឡើងនៅលើការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការ: SendGroupInvite public Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId) { return Runtime.Do(async () => { var result = await api.GroupServiceClient.SendGroupInviteAsync(/* ... */); if (!result.HasError) { State.OutgoingInvites.Add(inviteePlayerId); } return result; }); } ការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការ ជាមួយនឹងកំណត់ដំណឹងល្អ។ Task.Factory.StartNew លេខកូដ OK, ឥឡូវនេះយើងត្រូវការកាត់បន្ថយអ្វីគ្រប់យ៉ាងក្នុងខាងក្នុង ទូរស័ព្ទ; ហើយប៉ុន្តែវាបានដោះស្រាយបញ្ហានេះ, វាមានអត្ថប្រយោជន៍ក្នុងការបាត់បង់យ៉ាងងាយស្រួល, និងជាទូទៅវាជាការគួរឱ្យស្រស់ស្អាត។ Do សូមមើលផ្នែកនេះនៃកូដមួយទៀត: await leader.Group.SendGroupInvite(follower.PlayerId); នៅទីនេះ, bot របស់យើងមាន a សមត្ថភាព នេះគឺជាម៉ូឌុលដែលមានតែដើម្បីផ្លាស់ប្តូរកូដទៅជាកម្រិតផ្សេងគ្នានិងការពារពីការបំពាក់កូដ។ សត្វ។ Group Group Bot public class BotClient : BotClientBase { public GroupBotModule Group { get; } /* ... */ } សូមប្រាកដថា ម៉ូឌុលគួរតែមានអ៊ីនធឺណិត: public class BotClient : BotClientBase { public IGroupBotModule Group { get; } /* ... */ } public interface IGroupBotModule : IBotModule, IAsyncDisposable { GroupBotState State { get; } Task<SendGroupInviteResponse> SendGroupInvite(PlayerId toPlayerId); /* ... */ } public class GroupBotModule : BotClientModuleBase<GroupBotModule>, IGroupBotModule { /* ... */ public async Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId) { // no wrapping with `Runtime.Do` here var result = await api.GroupServiceClient.SendGroupInviteAsync(new() /* ... */); if (!result.HasError) { State.OutgoingInvites.Add(new GroupBotState.Invite(inviteePlayerId, /* ... */)); } return result; } | ហើយឥឡូវនេះវាតែធ្វើការ! មិនមែនជាកូដធ្លាក់ចុះនិងធ្លាក់ចុះទេប៉ុន្តែគ្រាន់តែជាតម្រូវការងាយស្រួល (ដែលជាធម្មតាសម្រាប់អ្នកអភិវឌ្ឍន៍) ដើម្បីបង្កើតអ៊ីនធឺណិតសម្រាប់ម៉ូឌុលទាំងអស់។ ទោះជាយ៉ាងណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏ដោយវាជាការពិតណាក៏។ ទូរស័ព្ទ : Runtime.Do public const string MethodProxy = @" public async $return_type$ $method_name$($method_params$) { $return$await runtime.Do(() => implementation.$method_name$($method_args$)); } "; ឧបករណ៍ ជំហានបន្ទាប់នេះគឺដើម្បីឧបករណ៍កូដដើម្បីជួបប្រជុំគ្នានិងតម្រូវការ។ ជាលើកដំបូង, ទាំងអស់នៃការទូរស័ព្ទ API នេះគួរតែត្រូវបានពិនិត្យឡើងវិញ។ ការផ្លាស់ប្តូរនេះគឺយ៉ាងងាយស្រួលជាការដឹកជញ្ជូនរបស់យើងមានប្រសិទ្ធិភាពធ្វើឱ្យប្រាកដថាជាប្រព័ន្ធ gRPC ដូច្នេះយើងគ្រាន់តែប្រើ interceptor ដែលបានសរសេរយ៉ាងត្រឹមត្រូវ... callInvoker = channel .Intercept(new PerformanceInterceptor()); កន្លែងដែល វាគឺជាការបង្វិល gRPC នៃការបង្វិល RPC របស់អតិថិជន។ CallInvoker បន្ទាប់មកវាគឺជាការល្អប្រសើរណាស់ដើម្បីកំណត់ផ្នែកមួយចំនួននៃកូដដើម្បីត្រួតពិនិត្យការអនុវត្ត។ ដូច្នេះគ្រប់ម៉ូដទាំងអស់ injects ជាមួយនឹងអ៊ីនធឺណិតខាងក្រោម: IInstrumentationFactory public interface IInstrumentationFactory { public IInstrumentationScope CreateScope( string? name = null, IInstrumentationContext? actor = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0); } ឥឡូវនេះអ្នកអាចបំពាក់ផ្នែកដែលអ្នកមានអារម្មណ៍នៅក្នុង: public Task AcceptGroupInvite(GroupInvideId inviteId) { using (instrumentationFactory.CreateScope()) { var result = await api.GroupServiceClient.AcceptGroupInviteAsync(request); } } ប្រសិនបើអ្នកអាចប្រើលក្ខណៈពិសេសនេះដើម្បីបង្កើតតំបន់បណ្ដាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញបណ្តាញ public const string MethodProxy = @" public async $return_type$ $method_name$($method_params$) { using(instrumentationFactory.CreateScope(/* related args> */)) { $return$await runtime.Do(() => implementation.$method_name$($method_args$)); } } "; ទំហំឧបករណ៍បានចុះឈ្មោះពេលវេលាការដោះស្រាយការប៉ុណ្ណោះបានបង្កើតដំណោះស្រាយចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយដំណោះស្រាយផ្សេងទៀត។ ការផ្សព្វផ្សាយ bot-to-bot គំរូនៅក្នុងតំបន់ "គោលបំណងរបស់យើង" មិនបង្ហាញអំពីការទំនាក់ទំនងទេ។ យើងគ្រាន់តែដឹងអំពី bot របស់អ្នកគាំទ្រ។ ទោះបីជាប្រភេទនេះនៃការសរសេរប្រសិនបើមានភាពងាយស្រួលនិងស្រស់ស្អាតនោះទេប៉ុន្តែមានប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រស PlayerId ខ្ញុំនៅដំបូងខ្ញុំមានគោលបំណងដើម្បីអនុវត្តប្រភេទមួយនៃគំរូ blackboard ជាមួយនឹងការផ្ទុកគោលបំណងគោលបំណងសម្រាប់ការទំនាក់ទំនង (ដូចជា Redis) ប៉ុន្តែបន្ទាប់ពីការធ្វើតេស្តគំនិតគោលបំណងមួយចំនួនបានបង្ហាញថាការធ្វើតេស្តអាចត្រូវបានកាត់បន្ថយយ៉ាងខ្លាំងទៅនឹងគោលបំណងពីរដែលមានភាពងាយស្រួលជាងមុន: ជម្រើសនិងអ្នកជ្រើស។ យោបល់ - Tickets នៅលើពិភពលោកពិតប្រាកដអ្នកលេងផ្លាស់ប្តូរជាមួយគ្នា - ពួកគេបានសរសេរសេចក្តីអធិប្បាយដោយផ្ទាល់ឬប្រើការសរសេរភ្ញាក់ផ្លាស់ប្តូរដោយភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ផ្លាស់ប្តូរភ្ញាក់ សូមអរគុណអ្នកដើម្បីចូលទៅក្នុងក្រុមរបស់ខ្ញុំ។ នៅទីនេះជាទីតាំងដែលសំបុត្រចូលទៅក្នុងការប្រកួត: មនុស្ស public interface ISwarmTickets { Task PlaceTicket(SwarmTicketBase ticket); Task<SwarmTicketBase?> TryGetTicket(Type ticketType, TimeSpan timeout); } កុំព្យូទ័រនេះអាចដំឡើងកុំព្យូទ័រមួយហើយបន្ទាប់មកកុំព្យូទ័រមួយផ្សេងទៀតអាចទាញយកកុំព្យូទ័រនេះ។ មិនមែនជាមិត្តភក្តិមិត្តភក្តិមិត្តភក្តិមិត្តភក្តិមិត្តភក្តិមិត្តភក្តិ (បច្ចុប្បន្ននេះគឺជា ច្រើនជាងនេះ, ដូច្នេះមានលក្ខណៈពិសេសបន្ថែមទៀតដើម្បីជួសជុល bots ពីការស្វែងរកតុល្យភាពរបស់ពួកគេដូចជាការធ្វើតេស្តតូចផ្សេងទៀត។ ISwarmTickets ticketType សត្វ ជាមួយនឹងអ៊ីនធឺណិតនេះយើងអាចផ្លាស់ប្តូរប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រសិនបើប្រ private async Task RunLeaderRole(ISwarmAgent swarm) { var ticket = await swarm.Blackboard.Tickets .TryGetTicket<BotWantsGroupTicket>(TimeSpan.FromSeconds(5)) .ThrowIfTicketIsNull(); await bot.Group.SendGroupInvite(ticket.Owner); await bot.Group.Subscription.ListenOnceUntil( GotInviteAcceptedEvent, TimeSpan.FromSeconds(5)) .ThrowIfTimeout(); } private async Task RunFollowerRole(ISwarmAgent swarm) { var waitingForInvite = bot.Group.Subscription.ListenOnceUntil( GotInviteAddedEvent, TimeSpan.FromSeconds(5)) .ThrowIfTimeout(); await swarm.Blackboard.Tickets.PlaceTicket(new BotWantsGroupTicket(bot.PlayerId)); await waitingForInvite; await bot.Group.AcceptGroupInvite(bot.Group.State.IncomingInvites[0].Id); } ជ្រើសរើស យើងមានដំណោះស្រាយពីរដំណោះស្រាយមួយសម្រាប់អ្នកគ្រប់គ្រងមួយសម្រាប់អ្នកគ្រប់គ្រងមួយសម្រាប់អ្នកគ្រប់គ្រងមួយ។ ដូចគ្នានេះវាអាចត្រូវបានផ្លាស់ប្តូរទៅក្នុងដំណោះស្រាយពីរដំណោះស្រាយផ្សេងគ្នានិងចាប់ផ្តើមតាមដំណោះស្រាយ។ ពេលវេលានេះគឺជាវិធីល្អបំផុតដើម្បីធ្វើដូច្នេះ, ពេលវេលាផ្សេងទៀតអ្នកអាចត្រូវការកំណត់ដំណោះស្រាយនៃទំហំក្រុម (អ្នកគ្រប់គ្រងជាច្រើនក្នុងមួយអ្នកគ្រប់គ្រងមួយ) ឬប្រព័ន្ធផ្សេងទៀតដើម្បីផ្លាស់ប្តូរ / ជ្រើសរើសទិន្នន័យ / ដំណោះស្រាយផ្សេងគ្នា។ public override async Task Run(ISwarmAgent swarm) { var roleAction = await swarm.Blackboard .RoundRobinRole( "leader or follower", Enumerable.Repeat(RunFollowerRole, config.GroupSize - 1).Union([RunLeaderRole])); await roleAction(swarm); } នៅទីនេះ វាគឺគ្រាន់តែជាការវេចខ្ចប់ស្រស់ស្អាតនៅជុំវិញការគណនានិងប្រតិបត្តិការ modulo ដើម្បីជ្រើសរើសអ៊ីម៉ែលត្រឹមត្រូវពីគណិត។ RoundRobinRole សត្វ Swarms ឥឡូវនេះ, ជាមួយនឹងការទំនាក់ទំនងទាំងអស់ដែលត្រូវបានចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែកចែក គោលបំណងដ៏អស្ចារ្យ: យើងមិនបានបញ្ចប់ការអនុវត្តនៃលក្ខណៈនេះទេ។ នៅពេលដែល QA បានទទួលបានការអនុវត្តរបស់ពួកគេនៅលើកុងតែមួយ SwarmAgent នៅពេលដែលពួកគេបានចាប់ផ្តើមចាប់ផ្តើមរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀបរៀប ការអនុវត្តតែមួយ តើធ្វើដូចម្តេចអំពីការធ្វើឱ្យប្រសើរឡើង? តើមានអ្វីដែលត្រូវបានបាត់បង់នៅក្នុងការវេចខ្ចប់ទាំងអស់និងការត្រួតពិនិត្យគ្នានៃការគិតថ្លៃ? ខ្ញុំមិននឹងបាត់បង់អ្នកជាមួយនឹងការធ្វើតេស្តផ្សេងៗទាំងអស់ដែលបានធ្វើឡើង, គ្រាន់តែពីរនៃការសំខាន់បំផុតដែលបានបង្ហាញថាប្រព័ន្ធនេះអាចបង្កើតផ្ទុកល្អឥតខ្ចោះ។ ការបង្កើត Benchmark: BenchmarkDotNet v0.14.0, ប្រព័ន្ធប្រតិបត្តិការ Windows 10 (10.0.19045.4651/22H2/2022Update) Intel Core i7-10875H CPU 2.30GHz, 1 CPU, 16 គោលនយោបាយនិង 8 គោលនយោបាយ ប្រព័ន្ធប្រតិបត្តិការ .NET SDK 9.0.203 ប្រព័ន្ធ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រព័ន្ធ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ private async Task SimpleWorker() { var tasks = new List<Task>(); for (var i = 0; i < Parallelism; ++i) { var index = i; tasks.Add(Task.Run(async () => { for (var j = 0; j < Iterations; ++j) { await LoadMethod(index); } })); } await Task.WhenAll(tasks); } private async Task SchedulerWorker() { // scenarios are prepared in GlobalSetup await Task.WhenAll(scenarios.Select(LaunchScenarioAsyncThread)); } public class TestScenario : ScenarioBase { public override async Task Run() { for (var iteration = 0; iteration < iterations; ++iteration) { await botClient.Do(BotLoadMethod); } } } សូមពិនិត្យឡើងវិញពីការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំនៃការរៀបចំ។ ក្នុងករណីនេះវិធីសាស្រ្តផ្ទុកទាំងពីរគឺដូចខាងក្រោម: private ValueTask CounterLoadMethod(int i) { Interlocked.Increment(ref StaticControl.Counter); return ValueTask.CompletedTask; } លទ្ធផលសម្រាប់ Iterations = 10: WorkerType Parallelism Mean Error StdDev Gen0 Gen1 Gen2 Allocated Simple 10 3.565us 0.0450us 0.0421us 0.3433 - - 2.83KB Simple 100 30.281us 0.2720us 0.2544us 3.1128 0.061 - 25.67KB Simple 1000 250.693us 2.1626us 2.4037us 30.2734 5.8594 - 250.67KB Scheduler 10 40.629us 0.7842us 0.8054us 8.1787 0.1221 - 66.15KB Scheduler 100 325.386us 2.3414us 2.1901us 81.0547 14.6484 - 662.09KB Scheduler 1000 4,685.812us 24.7917us 21.9772us 812.5 375 - 6617.59KB ដំបូង 10 565 លទ្ធផល លទ្ធផល 0.0450 00421 លទ្ធផល 0.3433 - - 8.8 គីឡូក្រាម ដំបូង 100 លទ្ធផល 281 0.2720 លទ្ធផល 0.2544us 3.1128 0.061 - 2567 គីឡូក្រាម ដំបូង 1000 លក្ខណៈពិសេស 1626 មីនាទី 2.4037 លទ្ធផល 30.2734 5.8594 - 250.67 គីឡូក្រាម សកម្មភាព 10 លទ្ធផល 629 លទ្ធផល 7842 លទ្ធផល 8054 8.1787 0.1221 - 615 គីឡូក្រាម សកម្មភាព 100 លទ្ធផល 386 2.3414 អាសយដ្ឋាន 2 ឆ្នាំ 1901 81.0547 14.6484 - លក្ខណៈពិសេស សកម្មភាព 1000 លក្ខណៈពិសេស 7277 អាសយដ្ឋាន 9277 អាសយដ្ឋាន 812.5 375 - លក្ខណៈពិសេស តើធ្វើដូចម្តេច? មិនត្រឹមត្រូវទេ។ មុនពេលធ្វើការធ្វើការសាកល្បងពិតប្រាកដខ្ញុំគិតថាការធ្វើការប្រសើរជាងមុនទេប៉ុន្តែផលិតផលពិតប្រាកដថាខ្ញុំបានសាកល្បងទេ។ សូមគិតអំពីអ្វីដែលបានធ្វើឡើងនៅក្រោមការសាកល្បងនិងអត្ថប្រយោជន៍ដែលយើងបានទទួលបានតែសម្រាប់ ~4us ក្នុងមួយប្រវត្តិសាស្រ្តដទៃ។ ដូច្នេះវាគឺជាការបង្ហាញតែមួយនៃការប្រសើរឡើងវិញ។ យើងមានអារម្មណ៍ក្នុងគោលបំណងបន្ថែមទៀត។ អ្វីដែលអាចជាប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រស ប្រព័ន្ធ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ ប្រតិបត្តិការ សម្រាប់ភាពងាយស្រួលរបស់យើង, RPC client ដែលត្រូវបានរក្សាទុកនៅខាងក្រៅនៃ bots ។ PingAsync private async ValueTask PingLoadMethod(int i) { await clients[i].PingAsync(new PingRequest()); } Here are the results, again 10 iterations (server grpc គឺនៅក្នុងបណ្តាញផ្ទាល់ខ្លួន): WorkerType Parallelism Mean Error StdDev Gen0 Gen1 Allocated Simple 100 94.45 ms 1.804 ms 2.148 ms 600 200 6.14 MB Simple 1000 596.69 ms 15.592 ms 45.730 ms 9000 7000 76.77 MB Scheduler 100 95.48 ms 1.547 ms 1.292 ms 833.3333 333.3333 6.85 MB Scheduler 1000 625.52 ms 14.697 ms 42.405 ms 8000 7000 68.57 MB ដំបូង 100 លក្ខណៈពិសេស 94.45 ms 804 មីនាទី 284 មីនាទី 600 200 6.14 មេកាបៃ ដំបូង 1000 69.69 មីនាទី 15 592 មីនាទី 45730 មីនាទី 9000 7000 767 មេកាបៃ សកម្មភាព 100 95.48 មីនាទី 1547 គីឡូក្រាម 1,292 មីនាទី 833.3333 333.3333 6.85 មេកាបៃ សកម្មភាព 1000 625 គីឡូក្រាម 14.697 មីនាទី 4405 មីនាទី 8000 7000 លក្ខណៈសម្បត្តិ 68.57 MB ដូចគ្នានៅពេលដែលគួរឱ្យចាប់អារម្មណ៍ថានៅពេលដែលផលប៉ះពាល់នៃការធ្វើឱ្យប្រសើរឡើងដល់ប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមានប្រសិនបើមាន។ ការពិនិត្យ លក្ខណៈពិសេសរបស់ Swarm គឺជាការពិតប្រាកដដែលអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកអាចធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិនបើអ្នកធ្វើឱ្យប្រសិន នេះគឺជាឧទាហរណ៍នៃគំរូផ្សេងគ្នានៅក្នុងការធ្វើតេស្តបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានៃការបញ្ហានរបស់អ្នក។ យើងជាធម្មតាត្រូវបានកាត់បន្ថយការធ្វើតេស្តយ៉ាងពេញលេញបន្ទាប់ពីបញ្ហាសម្រាប់ចំនួនមួយចំនួននៃបញ្ហាសម្រាប់ការធ្វើតេស្តនេះ, ដូច្នេះការទូរស័ព្ទ API បានបញ្ចប់យ៉ាងឆាប់រហ័ស។ លក្ខណៈពិសេសនៃការធ្វើតេស្ត Swarm គឺជាលក្ខណៈពិសេសនៃការធ្វើតេស្ត Swarm ។ សម្រាប់ការធ្វើតេស្ត Swarm, លក្ខណៈពិសេសនៃការធ្វើតេស្ត Swarm គឺជាលក្ខណៈពិសេសនៃការធ្វើតេស្ត Swarm ។ អ្វីដែលបានជួយក្នុងការ debugging ។ connectionIdD/botId Note: DevServer គឺជាការសាងសង់ monolith ដែលមានភាពងាយស្រួលនៃសេវាកម្ម backend ទាំងអស់នៅក្នុងមួយដែលនឹងត្រូវបានបើកដំណើរការនៅលើកុំព្យូទ័រអ្នកអភិវឌ្ឍន៍ហើយវាគឺជាឧទាហរណ៍ដែលត្រូវបានរចនាឡើងជាពិសេសដើម្បីកាត់បន្ថយទំហំនៃទិន្នន័យនៅលើអេក្រង់។ Swarm bots បានសរសេរប្រភេទផ្សេងទៀតនៃដំណាក់កាល: ពួកគេបានសរសេរដំណាក់កាលដែលត្រូវបានប្រើក្នុងការដំណាក់កាលដែលមានប្រសិទ្ធិភាព។ ជាមួយនឹងការជួយនៃឧបករណ៍ដែលមានប្រសិទ្ធិភាពអាចមើលនិងវិភាគដំណាក់កាលទាំងនេះដោយប្រើសមត្ថភាពដែលមានមូលដ្ឋានដូចជា PerfettoSQL ។ អ្វីដែលយើងបានបញ្ចប់នៅចុងក្រោយ ឥឡូវនេះយើងមាន SDK ដែលបំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់បំពង់ ការអាចសរសេរកូដដែលមានភាពងាយស្រួលបានអនុញ្ញាតឱ្យក្រុម QA បានបង្កើតម៉ូឌុលថ្មីនិងប្រវត្តិសាស្រ្តធ្វើតេស្តដែលមានភាពងាយស្រួល។ មានគំនិតអំពីប្រវត្តិសាស្រ្ត FlowGraph (កម្មវិធីរចនាសម្ព័ន្ធរូបភាព) ប៉ុន្តែវាមានគំនិតតែប៉ុណ្ណោះនៅពេលនេះ។ ការធ្វើតេស្តប្រសិទ្ធិភាពគឺជាការរំខាន - ខ្ញុំបាននិយាយយ៉ាងហោចណាស់ដោយសារតែអ្នកគួរតែចាប់ផ្តើមពួកគេដោយផ្ទាល់។ Swarm មិនគ្រាន់តែជួយជាមួយនឹងការធ្វើតេស្តទេប៉ុន្តែវាត្រូវបានប្រើជាធម្មតានៅក្នុងការធ្វើតេស្តនិងដោះស្រាយបញ្ហានៅក្នុងការលេងហ្គេម, ជាពិសេសនៅពេលដែលការធ្វើតេស្តនេះមានភាពងាយស្រួលក្នុងការធ្វើតេស្តដោយដៃដូចជាពេលដែលអ្នកត្រូវការអតិថិជនជាច្រើនក្នុងការធ្វើតេស្តឯកទេស។ សូមបញ្ជាក់ថាយើងមានអារម្មណ៍ខ្លាំងណាស់អំពីការធ្វើតេស្តរបស់យើងហើយយើងមិនមានអារម្មណ៍អំពីការធ្វើតេស្តរបស់យើង។ ខ្ញុំចង់ឱ្យអ្នករីករាយជាមួយសៀវភៅនេះ, វាគឺជាការជោគជ័យដើម្បីបង្ហាញអ្នកអំពីការអភិវឌ្ឍន៍ Swarm ទាំងអស់ដោយមិនធ្វើឱ្យសៀវភៅនេះបានបំបែកយ៉ាងខ្លាំង។ ខ្ញុំមានសុវត្ថិភាពថាខ្ញុំអាចបាត់បន្ថយព័ត៌មានសំខាន់មួយចំនួននៅក្នុងដំណើរការនៃការកំណត់ទំហំសៀវភៅនិងព័ត៌មានប៉ុន្តែខ្ញុំនឹងផ្តល់ឱ្យអ្នកនូវអារម្មណ៍បន្ថែមទៀតប្រសិនបើអ្នកមានសំណួរណាមួយ!