.NET: multithreading සහ asynchrony සමඟ වැඩ කිරීම සඳහා මෙවලම්. 1 කොටස

මම හබ්ර් පිළිබඳ මුල් ලිපිය ප්‍රකාශයට පත් කරමි, එහි පරිවර්තනය සංස්ථාපිතයේ පළ කර ඇත බ්ලොග් සටහන.

මෙහි සහ දැන් ප්‍රතිඵලය එනතෙක් බලා නොසිට අසමමුහුර්තව යමක් කිරීමේ අවශ්‍යතාවය හෝ එය සිදුකරන ඒකක කිහිපයකට විශාල වැඩ බෙදා දීමේ අවශ්‍යතාවය පරිගණක පැමිණීමට පෙර පැවතිණි. ඔවුන්ගේ පැමිණීමත් සමඟ මෙම අවශ්‍යතාවය ඉතා ප්‍රත්‍යක්ෂ විය. දැන්, 2019 දී, මම මෙම ලිපිය ටයිප් කරන්නේ 8-core Intel Core ප්‍රොසෙසරයක් සහිත ලැප්ටොප් එකක, එහි ක්‍රියාවලි සියයකට වඩා සමාන්තරව ක්‍රියාත්මක වන අතර ඊටත් වඩා නූල් ද ඇත. ඒ අසල, මීට වසර කිහිපයකට පෙර මිලදී ගත් තරමක් අබලන් වූ දුරකථනයක් ඇත, එහි 8-core ප්‍රොසෙසරයක් ඇත. තේමාත්මක සම්පත් ඔවුන්ගේ කතුවරුන් 16-core ප්‍රොසෙසර සහිත මෙම වසරේ ප්‍රමුඛතම ස්මාර්ට්ෆෝන් අගය කරන ලිපි සහ වීඩියෝ වලින් පිරී ඇත. MS Azure 20 core ප්‍රොසෙසරයක් සහ 128 TB RAM සහිත අතථ්‍ය යන්ත්‍රයක් $2/පැයට වඩා අඩු මුදලකට සපයයි. අවාසනාවකට මෙන්, නූල් වල අන්තර්ක්‍රියා කළමනාකරණය කිරීමට නොහැකිව උපරිමය උකහා ගැනීමට සහ මෙම බලය උපයෝගී කර ගැනීමට නොහැකි ය.

පාරිභාෂිතය

ක්රියාවලිය - OS වස්තුව, හුදකලා ලිපින අවකාශය, නූල් අඩංගු වේ.
නූල් - OS වස්තුවක්, ක්‍රියාත්මක කිරීමේ කුඩාම ඒකකය, ක්‍රියාවලියක කොටසක්, නූල් ක්‍රියාවලියක් තුළ මතකය සහ අනෙකුත් සම්පත් තමන් අතර බෙදා ගනී.
බහු කාර්යයන් - OS දේපල, ක්‍රියාවලි කිහිපයක් එකවර ක්‍රියාත්මක කිරීමේ හැකියාව
බහු-core - ප්‍රොසෙසරයේ දේපලක්, දත්ත සැකසීම සඳහා හර කිහිපයක් භාවිතා කිරීමේ හැකියාව
බහු සැකසුම් - පරිගණකයක දේපලක්, භෞතිකව ප්‍රොසෙසර කිහිපයක් සමඟ එකවර වැඩ කිරීමේ හැකියාව
බහු කියවීම - ක්‍රියාවලියක දේපලක්, නූල් කිහිපයක් අතර දත්ත සැකසුම් බෙදා හැරීමේ හැකියාව.
සමාන්තරවාදය - කාල ඒකකයකට එකවර භෞතිකව ක්‍රියා කිහිපයක් සිදු කිරීම
අසමමිතිය - මෙම සැකසීම අවසන් වන තෙක් බලා නොසිට මෙහෙයුමක් ක්රියාත්මක කිරීම; ක්රියාත්මක කිරීමේ ප්රතිඵලය පසුව සැකසිය හැක.

රූපකය

සියලුම නිර්වචන හොඳ නොවන අතර සමහර ඒවාට අමතර පැහැදිලි කිරීමක් අවශ්‍ය වේ, එබැවින් මම විධිමත් ලෙස හඳුන්වා දුන් පාරිභාෂිතයට උදේ ආහාරය පිසීම පිළිබඳ රූපකයක් එක් කරමි. මෙම රූපකය තුළ උදෑසන ආහාරය පිසීම ක්රියාවලියකි.

උදෑසන උදෑසන ආහාරය පිළියෙළ කරන අතරතුර මම (CPU) මම කුස්සියට එනවා (පරිගණකය) මට අත් දෙකක් තියෙනවා (කටාරම්) මුළුතැන්ගෙයෙහි උපාංග ගණනාවක් තිබේ (IO): උඳුන, කේතලය, ටෝස්ටර්, ශීතකරණය. මම ගෑස් සක්‍රිය කර, එය මත කබලෙන් ලිපට තබා එය රත් වන තෙක් බලා නොසිට එයට තෙල් වත් කරමි (අසමමුහුර්තව, අවහිර නොවන-IO-වේට්), මම බිත්තර ශීතකරණයෙන් පිටතට ගෙන ඒවා පිඟානකට කඩා, පසුව එක් අතකින් පහර දෙන්න (නූල් # 1), සහ දෙවන (නූල් # 2) තහඩුව අල්ලාගෙන සිටීම (බෙදාගත් සම්පත්). දැන් මම කේතලය සක්‍රිය කිරීමට කැමතියි, නමුත් මට ප්‍රමාණවත් තරම් අත් නැත (නූල් සාගින්න) මෙම කාලය තුළ, කබලෙන් ලිපට උණුසුම් වේ (ප්රතිඵලය සැකසීම) මම කසයෙන් තළා ඇති දේ වත් කරමි. මම කේතලය වෙත අත දිගු කර එය ක්‍රියාත්මක කර එහි ජලය උතුරන ආකාරය මෝඩ ලෙස බලා සිටිමි (අවහිර කිරීම-IO-රැඳී සිටීම), මෙම කාලය තුළ ඔහු ඔම්ලට් කසයෙන් තැබූ පිඟාන සෝදාගත හැකි වුවද.

මම අත් දෙකෙන් ඔම්ලට් එකක් හැදුවා, මට වැඩිය නැහැ, නමුත් ඒ සමඟම, ඔම්ලට් එක කස පහර දෙන මොහොතේ, මෙහෙයුම් 2 ක් එකවර සිදු විය: ඔම්ලට් කසය, පිඟාන අල්ලා ගැනීම, කබලෙන් ලිපට රත් කිරීම. CPU යනු පරිගණකයේ වේගවත්ම කොටසයි, IO යනු බොහෝ විට සියල්ල මන්දගාමී වන දෙයයි, එබැවින් බොහෝ විට ඵලදායී විසඳුමක් වන්නේ IO වෙතින් දත්ත ලබා ගන්නා අතරතුර CPU හි යම් දෙයක් අල්ලා ගැනීමයි.

රූපකය දිගටම කරගෙන යාම:

  • ඔම්ලට් සකස් කිරීමේ ක්‍රියාවලියේදී, මම ඇඳුම් මාරු කිරීමට ද උත්සාහ කරමි, මෙය බහු කාර්ය සඳහා උදාහරණයක් වනු ඇත. වැදගත් සූක්ෂ්මතාවයක්: මිනිසුන්ට වඩා පරිගණක මේ සඳහා වඩා හොඳය.
  • සූපවේදීන් කිහිප දෙනෙකු සමඟ මුළුතැන්ගෙයක්, උදාහරණයක් ලෙස අවන්හලක - බහු-core පරිගණකයක්.
  • සාප්පු මධ්යස්ථානයක ආහාර ශාලාවක බොහෝ අවන්හල් - දත්ත මධ්යස්ථානය

.NET මෙවලම්

.NET ත්‍රෙඩ් සමඟ වැඩ කිරීමට දක්‍ෂයි, වෙනත් බොහෝ දේවල් සමඟ. සෑම නව අනුවාදයක් සමඟම, එය ඔවුන් සමඟ වැඩ කිරීම සඳහා වැඩි වැඩියෙන් නව මෙවලම්, OS නූල් හරහා වියුක්ත කිරීමේ නව ස්ථර හඳුන්වා දෙයි. වියුක්ත කිරීම් ගොඩනැගීම සමඟ වැඩ කරන විට, රාමු සංවර්ධකයින් ඉහළ මට්ටමේ වියුක්තයක් භාවිතා කරන විට, මට්ටම් එකක් හෝ කිහිපයක් පහළට යාමට අවස්ථාව ලබා දෙන ප්‍රවේශයක් භාවිතා කරයි. බොහෝ විට මෙය අවශ්‍ය නොවේ, ඇත්ත වශයෙන්ම එය තුවක්කුවකින් පාදයට වෙඩි තැබීමට දොර විවර කරයි, නමුත් සමහර විට, දුර්ලභ අවස්ථාවන්හිදී, වර්තමාන වියුක්ත මට්ටමේදී විසඳා නොමැති ගැටළුවක් විසඳීමට ඇති එකම ක්‍රමය එය විය හැකිය. .

මෙවලම් මගින්, මම අදහස් කරන්නේ රාමුව සහ තෙවන පාර්ශ්ව පැකේජ මගින් සපයන යෙදුම් ක්‍රමලේඛන අතුරුමුහුණත් (API) මෙන්ම බහු-නූල් කේත සම්බන්ධ ඕනෑම ගැටළුවක් සඳහා සෙවීම සරල කරන සම්පූර්ණ මෘදුකාංග විසඳුම් ය.

ත්‍රෙඩ් එකක් පටන් ගන්නවා

නූල් සමඟ වැඩ කිරීම සඳහා .NET හි ඇති මූලිකම පන්තිය වන්නේ නූල් පන්තියයි. ඉදිකිරීම්කරු නියෝජිතයින් දෙදෙනෙකුගෙන් එකක් පිළිගනී:

  • ThreadStart - පරාමිති නොමැත
  • ParametrizedThreadStart - වර්ගයේ වස්තුවක එක් පරාමිතියක් සමඟ.

ස්ටාට් ක්‍රමය ඇමතීමෙන් පසු අලුතින් සාදන ලද ත්‍රෙඩ් එකෙහි නියෝජිතයා ක්‍රියාත්මක කරනු ලැබේ.පරාමිතික ට්‍රෙඩ්ස්ටාට් වර්ගයේ නියෝජිතයෙකු කන්ස්ට්‍රක්ටරය වෙත ලබා දුන්නේ නම්, එවිට වස්තුවක් ආරම්භක ක්‍රමයට යැවිය යුතුය. ඕනෑම දේශීය තොරතුරක් ප්‍රවාහයට මාරු කිරීමට මෙම යාන්ත්‍රණය අවශ්‍ය වේ. නූල් නිර්මාණය කිරීම මිල අධික මෙහෙයුමක් බව සඳහන් කිරීම වටී, නූල් ම බර වස්තුවක් වන අතර, අඩුම තරමින් එය තොගය මත 1MB මතකයක් වෙන් කරන අතර OS API සමඟ අන්තර්ක්රියා අවශ්ය වේ.

new Thread(...).Start(...);

ThreadPool පන්තිය නියෝජනය කරන්නේ තටාකයක සංකල්පයයි. .NET හි, නූල් සංචිතය ඉංජිනේරුමය අංගයක් වන අතර, මයික්‍රොසොෆ්ට් හි සංවර්ධකයින් එය විවිධාකාර අවස්ථා වලදී ප්‍රශස්ත ලෙස ක්‍රියා කරන බවට වග බලා ගැනීමට විශාල උත්සාහයක් ගෙන ඇත.

පොදු සංකල්පය:

යෙදුම ආරම්භ වන මොහොතේ සිට, එය පසුබිමේ රක්ෂිතයේ නූල් කිහිපයක් නිර්මාණය කර ඒවා භාවිතය සඳහා ගැනීමට හැකියාව ලබා දෙයි. නූල් නිතර සහ විශාල වශයෙන් භාවිතා කරන්නේ නම්, ඇමතුම්කරුගේ අවශ්යතා සපුරාලීම සඳහා සංචිතය පුළුල් වේ. නියම වේලාවට තටාකයේ නොමිලේ නූල් නොමැති විට, එය එක් නූල් එකක් ආපසු එන තෙක් බලා සිටිනු ඇත, නැතහොත් නව එකක් සාදනු ඇත. නූල් සංචිතය සමහර කෙටි කාලීන ක්‍රියා සඳහා විශිෂ්ට වන අතර යෙදුමේ සමස්ත ක්‍රියාකාරිත්වය පුරාම සේවා ලෙස ක්‍රියාත්මක වන මෙහෙයුම් සඳහා දුර්වල ලෙස සුදුසු බව එයින් කියවේ.

සංචිතයෙන් නූල් භාවිතා කිරීම සඳහා, ParametrizedThreadStart ට සමාන අත්සනක් ඇති WaitCallback වර්ගයේ නියෝජිතයෙකු පිළිගන්නා QueueUserWorkItem ක්‍රමයක් ඇත, සහ එයට ලබා දුන් පරාමිතිය එකම කාර්යයක් ඉටු කරයි.

ThreadPool.QueueUserWorkItem(...);

අඩු ප්‍රසිද්ධ නූල් සංචිත ක්‍රමය RegisterWaitForSingleObject අවහිර නොවන IO මෙහෙයුම් සංවිධානය කිරීමට භාවිතා කරයි. WaitHandle ක්‍රමයට සම්මත වූ විට මෙම ක්‍රමයට සම්මත වූ නියෝජිතයා කැඳවනු ලැබේ.

ThreadPool.RegisterWaitForSingleObject(...)

.NET ට නූල් ටයිමරයක් ඇති අතර එය WinForms/WPF ටයිමරයට වඩා වෙනස් වන අතර එහි හසුරුවන්නා සංචිතයෙන් ගත් නූල් මත කැඳවනු ලැබේ.

System.Threading.Timer

ක්‍රියාත්මක කිරීම සඳහා නියෝජිතයෙකු සංචිතයෙන් නූල් වෙත යැවීමට තරමක් විදේශීය ක්‍රමයක් ද ඇත - BeginInvoke ක්‍රමය.

DelegateInstance.BeginInvoke

Kernel32.dll Win32 API වෙතින් CreateThread - ඉහත ක්‍රම බොහොමයක් හැඳින්විය හැකි කාර්යය ගැන මම කෙටියෙන් වාසය කිරීමට කැමැත්තෙමි. මෙම ශ්රිතය ඇමතීමට බාහිර ක්රමවල යාන්ත්රණයට ස්තුති වන්නට ක්රමයක් තිබේ. මම එවැනි ඇමතුමක් දුටුවේ එක් වරක් පමණක් උරුම කේතය පිළිබඳ භයානක උදාහරණයක වන අතර, මෙය හරියටම කළ කතුවරයාගේ අභිප්‍රේරණය තවමත් මට අභිරහසක්ව පවතී.

Kernel32.dll CreateThread

නූල් බැලීම සහ නිදොස් කිරීම

ඔබ විසින් නිර්මාණය කරන ලද නූල්, සියලුම තෙවන පාර්ශ්ව සංරචක සහ .NET සංචිතය Visual Studio හි Threads කවුළුවෙන් බැලිය හැක. මෙම කවුළුව යෙදුම නිදොස්කරණයට ලක්ව ඇති විට සහ විවේක ප්‍රකාරයේදී පමණක් නූල් තොරතුරු පෙන්වයි. මෙහිදී ඔබට එක් එක් නූලෙහි අට්ටි නාම සහ ප්‍රමුඛතා පහසුවෙන් බලා ගත හැකි අතර, නිදොස්කරණය නිශ්චිත නූලකට මාරු කළ හැක. ත්‍රෙඩ් පන්තියේ ප්‍රමුඛතා ගුණාංගය භාවිතා කරමින්, ඔබට ත්‍රෙඩ් එකක ප්‍රමුඛතාවය සැකසිය හැක, එය නූල් අතර ප්‍රොසෙසර කාලය බෙදීමේදී OC සහ CLR නිර්දේශයක් ලෙස සලකනු ඇත.

.NET: multithreading සහ asynchrony සමඟ වැඩ කිරීම සඳහා මෙවලම්. 1 කොටස

කාර්යය සමාන්තර පුස්තකාලය

Task Parallel Library (TPL) .NET 4.0 හි හඳුන්වා දෙන ලදී. දැන් එය අසමමුහුර්තකරණය සමඟ වැඩ කිරීම සඳහා සම්මත සහ ප්රධාන මෙවලම වේ. පැරණි ප්‍රවේශයක් භාවිතා කරන ඕනෑම කේතයක් උරුමයක් ලෙස සැලකේ. TPL හි මූලික ඒකකය වන්නේ System.Threading.Tasks නාම අවකාශයේ ඇති Task පන්තියයි. කාර්යයක් යනු නූල් මත වියුක්ත කිරීමකි. C# භාෂාවේ නව අනුවාදය සමඟ, අපට Tasks - async/await operators සමඟ වැඩ කිරීමට අලංකාර ක්‍රමයක් ලැබුණි. මෙම සංකල්ප මගින් අසමමුහුර්ත කේතය සරල හා සමමුහුර්ත ලෙස ලිවීමට හැකි විය, මෙය නූල්වල අභ්‍යන්තර ක්‍රියාකාරිත්වය පිළිබඳ අඩු අවබෝධයක් ඇති පුද්ගලයින්ට පවා ඒවා භාවිතා කරන යෙදුම් ලිවීමට හැකි විය, දිගු මෙහෙයුම් සිදු කරන විට කැටි නොවන යෙදුම්. async/await භාවිතා කිරීම ලිපි එකක් හෝ කිහිපයක් සඳහා මාතෘකාවකි, නමුත් මම එහි සාරාංශය වාක්‍ය කිහිපයකින් ලබා ගැනීමට උත්සාහ කරමි:

  • async යනු Task හෝ void නැවත ලබා දෙන ක්‍රමයක විකරණයකි
  • සහ waiit යනු අවහිර නොවන Task waiting operator වේ.

නැවත වරක්: බලා සිටීමේ ක්‍රියාකරු, සාමාන්‍ය අවස්ථාවෙහි (ව්‍යතිරේක පවතී), ක්‍රියාත්මක කිරීමේ වත්මන් නූල් තවදුරටත් මුදා හරිනු ඇත, සහ කාර්යය එහි ක්‍රියාත්මක කිරීම අවසන් වූ විට, සහ නූල් (ඇත්ත වශයෙන්ම, සන්දර්භය පැවසීම වඩාත් නිවැරදි වනු ඇත. , නමුත් ඒ ගැන වැඩි විස්තර පසුව) ක්‍රමය තවදුරටත් ක්‍රියාත්මක කරනු ඇත. .NET ඇතුළත, මෙම යාන්ත්‍රණය ක්‍රියාත්මක වන්නේ අස්වැන්න ප්‍රතිලාභ ලබා දෙන ආකාරයටම, ලිඛිත ක්‍රමය සම්පූර්ණ පන්තියක් බවට පත්වන විට, එය රාජ්‍ය යන්ත්‍රයක් වන අතර මෙම තත්වයන් අනුව වෙනම කැබලිවලට ක්‍රියාත්මක කළ හැකිය. උනන්දුවක් දක්වන ඕනෑම අයෙකුට Asynс/await භාවිතයෙන් ඕනෑම සරල කේතයක් ලිවීමට, Compiler Generated Code සක්‍රීය කර ඇති JetBrains dotPeek භාවිතයෙන් එකලස් කිරීම සම්පාදනය කිරීමට සහ බැලීමට හැකිය.

කාර්යය දියත් කිරීම සහ භාවිතා කිරීම සඳහා විකල්ප දෙස බලමු. පහත කේත උදාහරණයේ, අපි ප්‍රයෝජනවත් කිසිවක් නොකරන නව කාර්යයක් සාදන්නෙමු (නූල්. නින්ද (10000)), නමුත් සැබෑ ජීවිතයේ මෙය සංකීර්ණ CPU-දැඩි කාර්යයක් විය යුතුය.

using TCO = System.Threading.Tasks.TaskCreationOptions;

public static async void VoidAsyncMethod() {
    var cancellationSource = new CancellationTokenSource();

    await Task.Factory.StartNew(
        // Code of action will be executed on other context
        () => Thread.Sleep(10000),
        cancellationSource.Token,
        TCO.LongRunning | TCO.AttachedToParent | TCO.PreferFairness,
        scheduler
    );

    //  Code after await will be executed on captured context
}

විකල්ප ගණනාවක් සමඟ කාර්යයක් නිර්මාණය කර ඇත:

  • LongRunning යනු කාර්යය ඉක්මනින් අවසන් නොවන බවට ඉඟියක් වන අතර එයින් අදහස් කරන්නේ සංචිතයෙන් නූල් එකක් නොගෙන අනෙක් අයට හානියක් නොවන පරිදි මෙම කාර්යය සඳහා වෙනම එකක් නිර්මාණය කිරීම සලකා බැලීම වටී.
  • AttachedToParent - කාර්ය ධූරාවලියක් තුළ සකස් කළ හැක. මෙම විකල්පය භාවිතා කළේ නම්, කාර්යය එයම අවසන් කර එහි දරුවන් ක්‍රියාත්මක වන තෙක් බලා සිටින තත්වයක තිබිය හැකිය.
  • PreferFairness - යන්නෙන් අදහස් වන්නේ ක්‍රියාත්මක කිරීම සඳහා යවන ලද කාර්යයන් පසුව එවන ලද ඒවාට පෙර ක්‍රියාත්මක කිරීම වඩා හොඳ බවයි. නමුත් මෙය නිර්දේශයක් පමණක් වන අතර ප්රතිඵල සහතික නොවේ.

ක්‍රමයට ලබා දුන් දෙවන පරාමිතිය CancellationToken වේ. මෙහෙයුමක් ආරම්භ වූ පසු එය අවලංගු කිරීම නිවැරදිව හැසිරවීමට, ක්‍රියාත්මක වන කේතය CancellationToken තත්වය සඳහා චෙක්පත් වලින් පිරවිය යුතුය. චෙක්පත් නොමැති නම්, CancellationTokenSource වස්තුව මත කැඳවනු ලබන අවලංගු කිරීමේ ක්‍රමයට එය ආරම්භ වීමට පෙර පමණක් කාර්යය ක්‍රියාත්මක කිරීම නැවැත්වීමට හැකි වනු ඇත.

අවසාන පරාමිතිය TaskScheduler වර්ගයේ උපලේඛන වස්තුවකි. මෙම පන්තිය සහ එහි පැවත එන්නන් නූල් හරහා කාර්ය බෙදා හැරීමේ උපාය මාර්ග පාලනය කිරීමට සැලසුම් කර ඇත; පෙරනිමියෙන්, කාර්යය තටාකයේ සිට අහඹු නූල් මත ක්‍රියාත්මක වේ.

සාදන ලද කාර්යයට බලා සිටින ක්‍රියාකරු යොදනු ලැබේ, එයින් අදහස් වන්නේ එයට පසුව ලියා ඇති කේතය, එකක් තිබේ නම්, බලා සිටීමට පෙර කේතය හා සමාන සන්දර්භය තුළම (බොහෝ විට මෙයින් අදහස් කරන්නේ එකම නූල් මත) ක්‍රියාත්මක වේ.

ක්‍රමය async void ලෙස සලකුණු කර ඇත, එයින් අදහස් වන්නේ එය බලා සිටින ක්‍රියාකරු භාවිතා කළ හැකි නමුත් ඇමතුම් කේතය ක්‍රියාත්මක වන තෙක් රැඳී සිටීමට නොහැකි වනු ඇත. එවැනි විශේෂාංගයක් අවශ්‍ය නම්, ක්‍රමය මඟින් කාර්යය ආපසු ලබා දිය යුතුය. Async void ලෙස සලකුණු කරන ලද ක්‍රම බහුලව දක්නට ලැබේ: රීතියක් ලෙස, මේවා සිදුවීම් හසුරුවන්න හෝ ගින්න මත ක්‍රියා කරන සහ මූලධර්මය අමතක කරන වෙනත් ක්‍රම වේ. ඔබට ක්‍රියාත්මක කිරීම අවසන් වන තෙක් බලා සිටීමට අවස්ථාව ලබා දීම පමණක් නොව, ප්‍රති result ලය ආපසු ලබා දීමට අවශ්‍ය නම්, ඔබ කාර්යය භාවිතා කළ යුතුය.

StartNew ක්‍රමය ආපසු ලබා දුන් කාර්යයේදී මෙන්ම වෙනත් ඕනෑම අවස්ථාවක, ඔබට ව්‍යාජ පරාමිතිය සමඟ ConfigureAwait ක්‍රමය ඇමතීමට හැකිය, පසුව බලා සිටීමෙන් පසුව ක්‍රියාත්මක කිරීම ග්‍රහණය කරගත් සන්දර්භය මත නොව, අත්තනෝමතික එකක් මත දිගටම පවතිනු ඇත. බලා සිටීමෙන් පසු කේතය සඳහා ක්‍රියාත්මක කිරීමේ සන්දර්භය වැදගත් නොවන විට මෙය සැමවිටම කළ යුතුය. මෙය පුස්තකාලයක ඇසුරුම් කර බෙදා හරිනු ලබන කේතය ලිවීමේදී MS වෙතින් වන නිර්දේශයකි.

කාර්යයක් අවසන් වන තෙක් ඔබට බලා සිටිය හැක්කේ කෙසේද යන්න පිළිබඳව අපි තව ටිකක් වාසය කරමු. පහත දැක්වෙන්නේ, අපේක්ෂාව කොන්දේසි සහිතව හොඳින් සිදු කරන විට සහ කොන්දේසි සහිතව දුර්වල ලෙස සිදු කරන විට අදහස් දැක්වීම් සහිත කේතයේ උදාහරණයකි.

public static async void AnotherMethod() {

    int result = await AsyncMethod(); // good

    result = AsyncMethod().Result; // bad

    AsyncMethod().Wait(); // bad

    IEnumerable<Task> tasks = new Task[] {
        AsyncMethod(), OtherAsyncMethod()
    };

    await Task.WhenAll(tasks); // good
    await Task.WhenAny(tasks); // good

    Task.WaitAll(tasks.ToArray()); // bad
}

පළමු උදාහරණයේ දී, ඇමතුම් පොට අවහිර නොකර කාර්යය සම්පූර්ණ වන තෙක් අපි බලා සිටිමු; අපි ප්‍රතිඵලය සැකසීමට ආපසු යන්නේ එය දැනටමත් එහි ඇති විට පමණි; එතෙක්, ඇමතුම් නූල එහි උපාංග වෙත ඉතිරි වේ.

දෙවන විකල්පය තුළ, ක්රමයේ ප්රතිඵලය ගණනය කරන තෙක් අපි ඇමතුම් නූල් අවහිර කරමු. මෙය නරක වන්නේ අපි සරල උදාසීනත්වයකින්, වැඩසටහනේ එවැනි වටිනා සම්පතක් නූලක් අල්ලාගෙන ඇති නිසා පමණක් නොව, අප විසින් හඳුන්වන ක්‍රමයේ කේතයේ රැඳී සිටීම සහ සමමුහුර්ත කිරීමේ සන්දර්භයට පසුව ඇමතුම් නූල් වෙත නැවත පැමිණීම අවශ්‍ය වන බැවිනි. රැඳී සිටින්න, එවිට අපට අවහිරයක් ලැබෙනු ඇත: ඇමතුම් නූල් අසමමුහුර්ත ක්‍රමයේ ප්‍රතිඵලය ගණනය කිරීම සඳහා බලා සිටී, අසමමුහුර්ත ක්‍රමය ඇමතුම් නූල් තුළ එය ක්‍රියාත්මක කිරීමට නිෂ්ඵල උත්සාහ කරයි.

මෙම ප්රවේශයේ තවත් අවාසියක් වන්නේ සංකීර්ණ දෝෂ හැසිරවීමයි. කාරණය නම් async/await භාවිතා කරන විට අසමමුහුර්ත කේතයේ දෝෂ හැසිරවීමට ඉතා පහසු වේ - ඒවා කේතය සමමුහුර්ත වූවාක් මෙන් හැසිරේ. අපි කාර්යයකට සමමුහුර්ත භූතවාදය යෙදුවොත්, මුල් ව්‍යතිරේකය සමස්ථ ව්‍යතිරේකයක් බවට පත්වේ, i.e. ව්‍යතිරේකය හැසිරවීමට, ඔබට C# ලෝකයේ වඩාත් හුරුපුරුදු කැච් බ්ලොක් දාමය වෙනුවට InnerException වර්ගය පරීක්‍ෂා කර එක් අල්ලා ගැනීමේ කුට්ටියක් තුළ if දාමයක් ලිවීමට හෝ ගොඩනඟන විට අල්ලා ගැනීම භාවිතා කිරීමට සිදුවේ.

තුන්වන සහ අවසාන උදාහරණ එකම හේතුව නිසා නරක ලෙස සලකුණු කර ඇති අතර එකම ගැටළු අඩංගු වේ.

කාර්ය සමූහයක් බලා සිටීම සඳහා WhenAny සහ WhenAll ක්‍රම අතිශයින්ම පහසු වේ; ඔවුන් කාර්ය සමූහයක් එකකට ඔතා, එය කණ්ඩායමෙන් කාර්යයක් ප්‍රථම වරට ක්‍රියාත්මක වූ විට හෝ ඔවුන් සියල්ලන්ම ක්‍රියාත්මක කර අවසන් වූ විට වෙඩි තබනු ඇත.

නූල් නතර කිරීම

විවිධ හේතු නිසා, එය ආරම්භ වූ පසු ප්රවාහය නතර කිරීමට අවශ්ය විය හැකිය. මෙය කිරීමට ක්රම ගණනාවක් තිබේ. නූල් පන්තියට සුදුසු ලෙස නම් කරන ලද ක්‍රම දෙකක් ඇත: නවත්වන්න и බාධා කරන්න. පළමු එක භාවිතය සඳහා නිර්දේශ කර නැත, මන්ද ඕනෑම අහඹු මොහොතක එය ඇමතීමෙන් පසු, ඕනෑම උපදෙස් සැකසීමේදී, ව්යතිරේකයක් දමනු ලැබේ ThreadAbortedException. කිසියම් පූර්ණ සංඛ්‍යා විචල්‍යයක් වැඩි කිරීමේදී එවැනි ව්‍යතිරේකයක් ඔබ අපේක්ෂා නොකරයි, හරිද? තවද මෙම ක්රමය භාවිතා කරන විට, මෙය ඉතා සැබෑ තත්වයකි. ඔබට CLR කේතයේ යම් කොටසක එවැනි ව්‍යතිරේකයක් ජනනය කිරීමෙන් වැළැක්විය යුතු නම්, ඔබට එය ඇමතුම්වලින් ඔතා ගත හැක. Thread.BeginCriticalRegion, Thread.EndCriticalRegion. අවසාන බ්ලොක් එකක ලියා ඇති ඕනෑම කේතයක් එවැනි ඇමතුම් වල ඔතා ඇත. මෙම හේතුව නිසා, රාමු කේතයේ ගැඹුරේ ඔබට හිස් උත්සාහයකින් බ්ලොක් සොයාගත හැකිය, නමුත් අවසානයේ හිස් නොවේ. මයික්‍රොසොෆ්ට් මෙම ක්‍රමය කෙතරම් අධෛර්යමත් කරයිද යත් ඔවුන් එය .net core හි ඇතුළත් නොකළේය.

බාධා කිරීමේ ක්‍රමය වඩාත් පුරෝකථනය කළ හැකි ලෙස ක්‍රියා කරයි. එය ව්යතිරේකයක් සමඟ නූල් බාධා කළ හැකිය ThreadInterruptedException ත්‍රෙඩ් එක පොරොත්තු තත්වයක පවතින අවස්ථා වලදී පමණි. WaitHandle, lock, හෝ Thread.Sleep ඇමතීමෙන් පසු එය එල්ලී සිටින විට මෙම තත්වයට ඇතුල් වේ.

ඉහත විස්තර කර ඇති විකල්ප දෙකම ඔවුන්ගේ අනපේක්ෂිත බව නිසා නරක ය. විසඳුම ව්යුහයක් භාවිතා කිරීමයි CancellationToken සහ පන්තිය CancellationTokenSource. කාරණය මෙයයි: CancellationTokenSource පන්තියේ උදාහරණයක් නිර්මාණය කර ඇති අතර එය හිමි තැනැත්තාට පමණක් ක්‍රමය ඇමතීමෙන් මෙහෙයුම නැවැත්විය හැකිය. අවලංගු කරන්න. CancellationToken පමණක් මෙහෙයුම වෙත ලබා දෙයි. CancellationToken හිමිකරුවන්ට මෙම මෙහෙයුම අවලංගු කළ නොහැක, නමුත් පරීක්ෂා කළ හැක්කේ මෙහෙයුම අවලංගු කර ඇත්ද යන්න පමණි. මේ සඳහා බූලියන් දේපලක් ඇත අවලංගු කිරීම ඉල්ලා ඇත සහ ක්රමය විසිකිරීම අවලංගු කර ඇත. දෙවැන්න ව්යතිරේකයක් දමනු ඇත TaskCancelledException CancellationToken අවස්ථාව ගිරවීමේදී අවලංගු කිරීමේ ක්‍රමය කැඳවනු ලැබුවේ නම්. මම භාවිතා කිරීමට නිර්දේශ කරන ක්‍රමය මෙයයි. මෙය ව්‍යතිරේක මෙහෙයුමක් නවතා දැමිය හැක්කේ කුමන අවස්ථාවේදීද යන්න පිළිබඳ පූර්ණ පාලනය ලබා ගැනීමෙන් පෙර විකල්පයන්ට වඩා වැඩිදියුණු කිරීමකි.

නූලක් නැවැත්වීම සඳහා වඩාත්ම ම්ලේච්ඡ විකල්පය වන්නේ Win32 API TerminateThread ශ්‍රිතය ඇමතීමයි. මෙම කාර්යය ඇමතීමෙන් පසු CLR හි හැසිරීම අනපේක්ෂිත විය හැකිය. MSDN හි මෙම කාර්යය ගැන පහත ලියා ඇත: “ටර්මිනේට් ත්‍රෙඩ් භයානක කාර්යයක් වන අතර එය භාවිතා කළ යුත්තේ අතිශය ආන්තික අවස්ථාවන්හිදී පමණි. "

FromAsync ක්‍රමය භාවිතයෙන් Leagacy API Task Based බවට පරිවර්තනය කිරීම

Tasks හඳුන්වා දීමෙන් පසුව ආරම්භ කරන ලද ව්‍යාපෘතියක වැඩ කිරීමට ඔබ වාසනාවන්ත නම් සහ බොහෝ සංවර්ධකයින් සඳහා නිහඬ භීතියක් ඇති කිරීම නැවැත්වූයේ නම්, ඔබට පැරණි API විශාල ප්‍රමාණයක්, තෙවන පාර්ශවීය ඒවා සහ ඔබේ කණ්ඩායම සමඟ කටයුතු කිරීමට සිදු නොවේ. අතීතයේ වධ දී ඇත. වාසනාවකට මෙන්, .NET Framework කණ්ඩායම අපව රැකබලා ගත්තේය, සමහර විට ඉලක්කය අප ගැනම බලා ගැනීම විය හැකිය. එය එසේ වුවත්, .NET සතුව පැරණි අසමමුහුර්ත ක්‍රමලේඛන ප්‍රවේශයන් තුළ ලියා ඇති කේත වේදනා රහිතව නව එකට පරිවර්තනය කිරීම සඳහා මෙවලම් ගණනාවක් තිබේ. ඒවායින් එකක් වන්නේ TaskFactory හි FromAsync ක්රමයයි. පහත කේත උදාහරණයේ, මම මෙම ක්‍රමය භාවිතා කරමින් WebRequest පන්තියේ පැරණි අසමමුහුර්ත ක්‍රම Task එකක එතුමි.

object state = null;
WebRequest wr = WebRequest.CreateHttp("http://github.com");
await Task.Factory.FromAsync(
    wr.BeginGetResponse,
    we.EndGetResponse
);

මෙය උදාහරණයක් පමණක් වන අතර ඔබට මෙය ගොඩනඟන ලද වර්ග සමඟ කිරීමට සිදු නොවනු ඇත, නමුත් ඕනෑම පැරණි ව්‍යාපෘතියක් හුදෙක් IAsyncResult සහ EndDoSomething ක්‍රම ලබා දෙන BeginDoSomething ක්‍රමවලින් පිරී ඇත.

TaskCompletionSource පන්තිය භාවිතයෙන් Legalacy API Task Based වෙත පරිවර්තනය කරන්න

සලකා බැලිය යුතු තවත් වැදගත් මෙවලමක් වන්නේ පන්තියයි TaskCompletionSource. කාර්යයන්, අරමුණ සහ මෙහෙයුම් මූලධර්මය අනුව, එය මා ඉහත ලියා ඇති ThreadPool පන්තියේ RegisterWaitForSingleObject ක්‍රමය තරමක් සිහිපත් කරයි. මෙම පන්තිය භාවිතා කරමින්, ඔබට පැරණි අසමමුහුර්ත API පහසුවෙන් සහ පහසු ලෙස Tasks තුළ ඔතා ගත හැක.

මෙම අරමුණු සඳහා අදහස් කරන ලද TaskFactory පන්තියේ FromAsync ක්‍රමය ගැන මම දැනටමත් කතා කර ඇති බව ඔබ කියනු ඇත. Microsoft විසින් පසුගිය වසර 15 පුරාවට ඉදිරිපත් කරන ලද .net හි අසමමුහුර්ත මාදිලියේ සංවර්ධනය පිළිබඳ සම්පූර්ණ ඉතිහාසය මෙහිදී අපට මතක තබා ගැනීමට සිදු වනු ඇත: Task-Based Asynchronous Pattern (TAP) ට පෙර Asynchronous Programming Pattern (APP) විය. ක්රම ගැන විය ආරම්භයයමක් නැවත පැමිණේ IAsyncResult සහ ක්රම අවසානයDoSomething එය පිළිගන්නා අතර මෙම වසරවල උරුමය සඳහා FromAsync ක්‍රමය පරිපූර්ණයි, නමුත් කාලයත් සමඟ එය සිදුවීම් පාදක අසමමුහුර්ත රටාව මගින් ප්‍රතිස්ථාපනය විය (EAP), අසමමුහුර්ත මෙහෙයුම අවසන් වූ විට සිදුවීමක් මතු වනු ඇතැයි උපකල්පනය කරන ලදී.

TaskCompletionSource සිදුවීම් ආකෘතිය වටා ගොඩනගා ඇති Tasks සහ legacy API එතීමට පරිපූර්ණයි. එහි කාර්යයේ සාරය පහත පරිදි වේ: මෙම පන්තියේ වස්තුවකට Task වර්ගයේ පොදු දේපලක් ඇත, එහි තත්වය TaskCompletionSource පන්තියේ SetResult, SetException යනාදිය හරහා පාලනය කළ හැකිය. මෙම කාර්යය සඳහා පොරොත්තු ක්‍රියාකරු යෙදූ ස්ථානවල, එය TaskCompletionSource වෙත යොදන ක්‍රමයට අනුව ව්‍යතිරේකයකින් ක්‍රියාත්මක වේ හෝ අසාර්ථක වනු ඇත. එය තවමත් පැහැදිලි නැතිනම්, සමහර පැරණි EAP API TaskCompletionSource භාවිතා කරමින් Task එකක ඔතා ඇති මෙම කේත උදාහරණය දෙස බලමු: සිදුවීම ගිනිගත් විට, කාර්යය සම්පූර්ණ කළ තත්වයට මාරු කරනු ලැබේ, සහ බලා සිටින ක්‍රියාකරු යෙදූ ක්‍රමය මෙම කාර්යයට වස්තුව ලැබුණු පසු එය ක්‍රියාත්මක කිරීම නැවත ආරම්භ වේ ප්රතිඵලය.

public static Task<Result> DoAsync(this SomeApiInstance someApiObj) {

    var completionSource = new TaskCompletionSource<Result>();
    someApiObj.Done += 
        result => completionSource.SetResult(result);
    someApiObj.Do();

    result completionSource.Task;
}

TaskCompletionSource ඉඟි සහ උපක්‍රම

පැරණි API එතීම TaskCompletionSource භාවිතයෙන් කළ හැකි සියල්ල නොවේ. මෙම පන්තිය භාවිතා කිරීමෙන් නූල්වල නොපවතින කාර්යයන් මත විවිධ API නිර්මාණය කිරීමේ සිත්ගන්නා හැකියාවක් විවෘත වේ. ප්‍රවාහය, අපට මතක ඇති පරිදි, මිල අධික සම්පතක් වන අතර ඒවායේ සංඛ්‍යාව සීමිතය (ප්‍රධාන වශයෙන් RAM ප්‍රමාණය අනුව). මෙම සීමාව සංකීර්ණ ව්‍යාපාරික තර්කනය සහිත පටවන ලද වෙබ් යෙදුමක් සංවර්ධනය කිරීමෙන් පහසුවෙන් සාක්ෂාත් කරගත හැකිය. Long-Polling වැනි උපක්‍රමයක් ක්‍රියාත්මක කිරීමේදී මා කතා කරන අවස්ථා සලකා බලමු.

කෙටියෙන් කිවහොත්, උපක්‍රමයේ සාරය මෙයයි: ඔබට API වෙතින් එහි පැත්තෙන් සිදුවන සමහර සිදුවීම් පිළිබඳ තොරතුරු ලබා ගත යුතු අතර, API හට යම් හේතුවක් නිසා සිදුවීම වාර්තා කළ නොහැක, නමුත් ප්‍රාන්තය පමණක් ආපසු ලබා දිය හැකිය. මේවාට උදාහරණයක් වන්නේ WebSocket කාලයට පෙර හෝ කිසියම් හේතුවක් නිසා මෙම තාක්ෂණය භාවිතා කිරීමට නොහැකි වූ විට HTTP මත ගොඩනගා ඇති සියලුම API වේ. සේවාදායකයාට HTTP සේවාදායකයෙන් විමසිය හැක. HTTP සේවාදායකයට සේවාදායකයා සමඟ සන්නිවේදනය ආරම්භ කළ නොහැක. සරළ විසඳුමක් වන්නේ ටයිමරයක් භාවිතා කර සේවාදායකයට ඡන්ද විමසීමක් කිරීමයි, නමුත් මෙය සේවාදායකය මත අමතර බරක් සහ සාමාන්‍ය TimerInterval / 2 මත අමතර ප්‍රමාදයක් ඇති කරයි. මෙය මඟහරවා ගැනීම සඳහා, Long Polling නම් උපක්‍රමයක් සොයා ගන්නා ලදී, එයට ප්‍රතිචාරය ප්‍රමාද කිරීම ඇතුළත් වේ. කාල සීමාව අවසන් වන තුරු හෝ සිදුවීමක් සිදු වන තුරු සේවාදායකය. සිදුවීම සිදුවී ඇත්නම්, එය සකසනු ලැබේ, එසේ නොවේ නම්, ඉල්ලීම නැවත යවනු ලැබේ.

while(!eventOccures && !timeoutExceeded)  {

  CheckTimout();
  CheckEvent();
  Thread.Sleep(1);
}

නමුත් එවැනි විසඳුමක් සිදුවීම සඳහා රැඳී සිටින සේවාදායකයින්ගේ සංඛ්‍යාව වැඩි වූ වහාම භයානක බව ඔප්පු වනු ඇත, මන්ද ... එවැනි සෑම සේවාදායකයෙක්ම සිදුවීමක් සඳහා බලා සිටින සම්පූර්ණ නූලක් අල්ලා ගනී. ඔව්, සහ සිදුවීම ක්‍රියාත්මක වූ විට අපට අමතර 1ms ප්‍රමාදයක් ලැබේ, බොහෝ විට මෙය සැලකිය යුතු නොවේ, නමුත් මෘදුකාංගය විය හැකි ප්‍රමාණයට වඩා නරක වන්නේ ඇයි? අපි Thread.Sleep(1) ඉවත් කළහොත්, නිෂ්ඵල ලෙස අපි එක් ප්‍රොසෙසර හරයක් 100% idle, වැඩකට නැති චක්‍රයක භ්‍රමණය වේ. TaskCompletionSource භාවිතයෙන් ඔබට මෙම කේතය පහසුවෙන් ප්‍රතිනිර්මාණය කර ඉහත හඳුනාගෙන ඇති සියලුම ගැටලු විසඳා ගත හැක:

class LongPollingApi {

    private Dictionary<int, TaskCompletionSource<Msg>> tasks;

    public async Task<Msg> AcceptMessageAsync(int userId, int duration) {

        var cs = new TaskCompletionSource<Msg>();
        tasks[userId] = cs;
        await Task.WhenAny(Task.Delay(duration), cs.Task);
        return cs.Task.IsCompleted ? cs.Task.Result : null;
    }

    public void SendMessage(int userId, Msg m) {

        if (tasks.TryGetValue(userId, out var completionSource))
            completionSource.SetResult(m);
    }
}

මෙම කේතය නිෂ්පාදනයට සූදානම් නැත, නමුත් නිකම්ම නිරූපනයකි. එය සැබෑ අවස්ථා වලදී භාවිතා කිරීමට, ඔබට අවම වශයෙන්, කිසිවකු එය අපේක්ෂා නොකරන අවස්ථාවක පණිවිඩයක් පැමිණෙන විට තත්වය හැසිරවීමට අවශ්‍ය වේ: මෙම අවස්ථාවේදී, AsseptMessageAsync ක්‍රමය දැනටමත් සම්පූර්ණ කර ඇති කාර්යයක් ආපසු ලබා දිය යුතුය. මෙය වඩාත් පොදු අවස්ථාව නම්, ඔබට ValueTask භාවිතා කිරීම ගැන සිතා බැලිය හැකිය.

අපට පණිවිඩයක් සඳහා ඉල්ලීමක් ලැබුණු විට, අපි ශබ්දකෝෂයේ TaskCompletionSource නිර්මාණය කර තබමු, ඉන්පසුව මුලින්ම සිදුවන්නේ කුමක්දැයි බලා සිටින්න: නිශ්චිත කාල පරතරය කල් ඉකුත් වේ හෝ පණිවිඩයක් ලැබේ.

ValueTask: ඇයි සහ කෙසේද

Async/await operators, අස්වැන්න ප්‍රතිලාභ ක්‍රියාකරු මෙන්, ක්‍රමයෙන් රාජ්‍ය යන්ත්‍රයක් ජනනය කරයි, මෙය නව වස්තුවක් නිර්මාණය කිරීමකි, එය සෑම විටම පාහේ වැදගත් නොවේ, නමුත් දුර්ලභ අවස්ථාවන්හිදී එය ගැටළුවක් ඇති කළ හැකිය. මෙම නඩුව ඇත්ත වශයෙන්ම බොහෝ විට හැඳින්වෙන ක්රමයක් විය හැකිය, අපි තත්පරයකට ඇමතුම් දස සහ සිය දහස් ගණනක් ගැන කතා කරමු. එවැනි ක්‍රමයක් ලියා ඇත්තේ බොහෝ අවස්ථාවලදී එය සියලු පොරොත්තු ක්‍රම මගහැර ප්‍රතිඵලයක් ලබා දෙන ආකාරයට නම්, .NET මෙය ප්‍රශස්ත කිරීමට මෙවලමක් සපයයි - ValueTask ව්‍යුහය. එය පැහැදිලි කිරීම සඳහා, එහි භාවිතය පිළිබඳ උදාහරණයක් අපි බලමු: අපි බොහෝ විට යන හැඹිලියක් තිබේ. එහි සමහර අගයන් ඇත, පසුව අපි ඒවා ආපසු ලබා දෙන්නෙමු; එසේ නොවේ නම්, ඒවා ලබා ගැනීමට අපි මන්දගාමී IO වෙත යමු. මට දෙවැන්න අසමමුහුර්තව කිරීමට අවශ්‍යයි, එයින් අදහස් කරන්නේ සම්පූර්ණ ක්‍රමයම අසමමුහුර්ත බවයි. මේ අනුව, ක්‍රමය ලිවීමේ පැහැදිලි ක්‍රමය පහත පරිදි වේ:

public async Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return val;
    return await RequestById(id);
}

ටිකක් ප්‍රශස්ත කිරීමට ඇති ආශාව සහ මෙම කේතය සම්පාදනය කිරීමේදී රොස්ලින් කුමක් ජනනය කරයිද යන සුළු බිය නිසා, ඔබට මෙම උදාහරණය පහත පරිදි නැවත ලිවිය හැකිය:

public Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return Task.FromResult(val);
    return RequestById(id);
}

ඇත්ත වශයෙන්ම, මෙම නඩුවේ ප්‍රශස්ත විසඳුම වනුයේ උණුසුම් මාර්ගය ප්‍රශස්ත කිරීම, එනම්, අනවශ්‍ය ප්‍රතිපාදන නොමැතිව ශබ්ද කෝෂයෙන් අගයක් ලබා ගැනීම සහ GC මත පැටවීම, දුර්ලභ අවස්ථාවන්හිදී අපට තවමත් දත්ත සඳහා IO වෙත යාමට අවශ්‍ය වූ විටය. , සෑම දෙයක්ම පැරණි ක්‍රමයට එකතු කිරීමක් / අඩුවක් ලෙස පවතිනු ඇත:

public ValueTask<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return new ValueTask<string>(val);
    return new ValueTask<string>(RequestById(id));
}

අපි මෙම කේත කොටස දෙස සමීපව බලමු: හැඹිලියේ අගයක් තිබේ නම්, අපි ව්යුහයක් සාදන්නෙමු, එසේ නොමැතිනම් සැබෑ කාර්යය අර්ථවත් එකක් තුළ ඔතා ඇත. ඇමතුම් කේතය මෙම කේතය ක්‍රියාත්මක කළේ කුමන මාර්ගයේද යන්න සැලකිල්ලට නොගනී: ValueTask, C# වාක්‍ය ඛණ්ඩ දෘෂ්ටි කෝණයකින්, මෙම අවස්ථාවේදී සාමාන්‍ය කාර්යයක් ලෙසම හැසිරෙනු ඇත.

TaskSchedulers: කාර්ය දියත් කිරීමේ උපාය මාර්ග කළමනාකරණය කිරීම

මම සලකා බැලීමට කැමති ඊළඟ API පන්තියයි TaskScheduler සහ එහි ව්යුත්පන්න. ත්‍රෙඩ් හරහා Tasks බෙදා හැරීමේ උපාය මාර්ග කළමනාකරණය කිරීමේ හැකියාව TPL හට ඇති බව මම ඉහතින් සඳහන් කර ඇත. එවැනි උපාය මාර්ග TaskScheduler පන්තියෙන් පැවත එන්නන් තුළ අර්ථ දක්වා ඇත. ඔබට අවශ්‍ය විය හැකි ඕනෑම උපාය මාර්ගයක් පාහේ පුස්තකාලයෙන් සොයාගත හැකිය. Parallel ExtensionsExtras, මයික්‍රොසොෆ්ට් විසින් සංවර්ධනය කරන ලද නමුත් .NET හි කොටසක් නොවේ, නමුත් Nuget පැකේජයක් ලෙස සපයනු ලැබේ. ඒවායින් සමහරක් කෙටියෙන් බලමු:

  • CurrentThreadTaskScheduler - වත්මන් නූල් මත කාර්යයන් ක්රියාත්මක කරයි
  • LimitedConcurrencyLevelTaskScheduler - ඉදිකිරීම්කරු තුළ පිළිගනු ලබන N පරාමිතිය මගින් එකවර ක්‍රියාත්මක කරන කාර්යයන් ගණන සීමා කරයි.
  • OrderedTaskScheduler — LimitedConcurrencyLevelTaskScheduler(1) ලෙස අර්ථ දක්වා ඇත, එබැවින් කාර්යයන් අනුක්‍රමිකව ක්‍රියාත්මක වේ.
  • WorkStealing TaskScheduler - ක්රියාත්මක කරයි වැඩ-සොරකම් කිරීම කාර්යය බෙදාහැරීමේ ප්රවේශය. අත්යවශ්යයෙන්ම එය වෙනම ThreadPool වේ. .NET ThreadPool හි ස්ථිතික පන්තියක් වන අතර එය සියලුම යෙදුම් සඳහා එකක් වන ගැටළුව විසඳයි, එයින් අදහස් කරන්නේ වැඩසටහනේ එක් කොටසක අධික ලෙස පැටවීම හෝ වැරදි භාවිතය තවත් කොටසක අතුරු ආබාධ ඇති කළ හැකි බවයි. එපමණක් නොව, එවැනි දෝෂ ඇතිවීමට හේතුව තේරුම් ගැනීම අතිශයින් දුෂ්කර ය. එම. ThreadPool භාවිතය ආක්‍රමණශීලී සහ අනපේක්ෂිත විය හැකි වැඩසටහනේ කොටස්වල වෙනම WorkStealingTaskSchedulers භාවිතා කිරීමට අවශ්‍ය විය හැක.
  • QueuedTaskScheduler - ප්‍රමුඛතා පෝලිම් රීති අනුව කාර්යයන් ඉටු කිරීමට ඔබට ඉඩ සලසයි
  • ThreadPerTaskScheduler - එය මත ක්‍රියාත්මක කරන සෑම කාර්යයක් සඳහාම වෙනම නූල් සාදයි. නිම කිරීමට අනපේක්ෂිත ලෙස දිගු කාලයක් ගත වන කාර්යයන් සඳහා ප්රයෝජනවත් විය හැකිය.

හොඳ විස්තරයක් තියෙනවා ලිපියක් microsoft බ්ලොග් අඩවියේ TaskSchedulers ගැන.

Tasks හා සම්බන්ධ සෑම දෙයක්ම පහසුවෙන් නිදොස් කිරීම සඳහා, Visual Studio සතුව Tasks කවුළුවක් ඇත. මෙම කවුළුව තුළ ඔබට කාර්යයේ වත්මන් තත්ත්වය දැක ගත හැකි අතර දැනට ක්‍රියාත්මක වන කේත රේඛාව වෙත පනින්න.

.NET: multithreading සහ asynchrony සමඟ වැඩ කිරීම සඳහා මෙවලම්. 1 කොටස

PLinq සහ සමාන්තර පන්තිය

Tasks සහ ඒවා ගැන කියන හැමදේටම අමතරව .NET හි තවත් රසවත් මෙවලම් දෙකක් තිබේ: PLinq (Linq2Parallel) සහ සමාන්තර පන්තිය. පළමුවැන්න බහු නූල් මත සියලුම ලින්ක් මෙහෙයුම් සමාන්තරව ක්‍රියාත්මක කිරීමට පොරොන්දු වේ. WithDegreeOfParallelism දිගු කිරීමේ ක්‍රමය භාවිතයෙන් නූල් ගණන වින්‍යාස කළ හැක. අවාසනාවකට මෙන්, බොහෝ විට PLinq එහි පෙරනිමි මාදිලියේ සැලකිය යුතු වේගයක් ලබා දීම සඳහා ඔබේ දත්ත ප්‍රභවයේ අභ්‍යන්තරය පිළිබඳ ප්‍රමාණවත් තොරතුරු නොමැත, අනෙක් අතට, උත්සාහ කිරීමේ පිරිවැය ඉතා අඩුය: ඔබට පෙර AsParallel ක්‍රමය ඇමතීමට අවශ්‍ය වේ. ලින්ක් ක්‍රම දාමය සහ කාර්ය සාධන පරීක්ෂණ ධාවනය කරන්න. එපමනක් නොව, කොටස් යාන්ත්‍රණය භාවිතයෙන් ඔබගේ දත්ත ප්‍රභවයේ ස්වභාවය පිළිබඳ අමතර තොරතුරු PLinq වෙත ලබා දිය හැක. ඔබට තවත් කියවන්න පුළුවන් මෙහි и මෙහි.

සමාන්තර ස්ථිතික පන්තිය සමාන්තරව Foreach එකතුවක් හරහා පුනරාවර්තනය කිරීම, For loop එකක් ක්‍රියාත්මක කිරීම සහ සමාන්තර Invoke හි බහු නියෝජිතයන් ක්‍රියාත්මක කිරීම සඳහා ක්‍රම සපයයි. ගණනය කිරීම් අවසන් වන තුරු වත්මන් නූල් ක්රියාත්මක කිරීම නතර කරනු ලැබේ. අවසාන තර්කය ලෙස ParallelOptions සම්මත කිරීමෙන් නූල් ගණන වින්‍යාසගත කළ හැක. ඔබට විකල්ප භාවිතයෙන් TaskScheduler සහ CancellationToken ද නියම කළ හැක.

සොයා ගැනීම්

මගේ වාර්තාවේ ඇති කරුණු සහ ඉන් පසුව මගේ වැඩ කටයුතු අතරතුර මා රැස් කළ තොරතුරු මත පදනම්ව මම මෙම ලිපිය ලිවීමට පටන් ගත් විට, එය එතරම් දෙයක් වනු ඇතැයි මම බලාපොරොත්තු නොවෙමි. දැන් මම මේ ලිපිය ටයිප් කරන ටෙක්ස්ට් එඩිටරය 15 පිටුව ගියා කියලා අපහාසාත්මකව කිව්වම මම අතුරු ප්‍රතිඵල සාරාංශ කරන්නම්. වෙනත් උපක්‍රම, APIs, දෘශ්‍ය මෙවලම් සහ අන්තරායන් ඊළඟ ලිපියෙන් ආවරණය කෙරේ.

නිගමන:

  • නවීන පරිගණකවල සම්පත් භාවිතා කිරීම සඳහා නූල්, අසමමුහුර්තකරණය සහ සමාන්තරකරණය සමඟ වැඩ කිරීමේ මෙවලම් ඔබ දැනගත යුතුය.
  • .NET මෙම අරමුණු සඳහා විවිධ මෙවලම් ඇත
  • ඒවා සියල්ලම එකවර දර්ශනය නොවීය, එබැවින් ඔබට බොහෝ විට පැරණි ඒවා සොයාගත හැකිය, කෙසේ වෙතත්, වැඩි උත්සාහයකින් තොරව පැරණි API පරිවර්තනය කිරීමට ක්රම තිබේ.
  • .NET හි නූල් සමඟ වැඩ කිරීම Thread සහ ThreadPool පන්ති මගින් නියෝජනය වේ
  • Thread.Abort, Thread.Interrupt, සහ Win32 API TerminateThread ක්‍රම භයානක වන අතර ඒවා භාවිතය සඳහා නිර්දේශ නොකරයි. ඒ වෙනුවට, CancellationToken යාන්ත්රණය භාවිතා කිරීම වඩා හොඳය
  • ප්රවාහය වටිනා සම්පතක් වන අතර එහි සැපයුම සීමිතය. සිදුවීම් සඳහා පොරොත්තුවෙන් නූල් කාර්යබහුල වන අවස්ථා වළක්වා ගත යුතුය. මේ සඳහා TaskCompletionSource පන්තිය භාවිතා කිරීම පහසුය
  • සමාන්තරකරණය සහ අසමමුහුර්තතාවය සමඟ වැඩ කිරීම සඳහා වඩාත් බලවත් සහ උසස් .NET මෙවලම් වන්නේ Tasks ය.
  • c# async/await ක්‍රියාකරුවන් අවහිර නොවන බලා සිටීමේ සංකල්පය ක්‍රියාත්මක කරයි
  • ඔබට TaskScheduler-ව්‍යුත්පන්න පන්ති භාවිතයෙන් නූල් හරහා කාර්ය බෙදා හැරීම පාලනය කළ හැක
  • උණුසුම් මාර්ග සහ මතක ගමනාගමනය ප්‍රශස්ත කිරීමට ValueTask ව්‍යුහය ප්‍රයෝජනවත් විය හැක
  • විෂුවල් ස්ටූඩියෝ හි කාර්යයන් සහ නූල් කවුළු බහු-නූල් හෝ අසමමුහුර්ත කේතය නිදොස් කිරීම සඳහා ප්‍රයෝජනවත් තොරතුරු රාශියක් සපයයි.
  • PLinq යනු සිසිල් මෙවලමකි, නමුත් එහි ඔබගේ දත්ත මූලාශ්‍රය පිළිබඳ ප්‍රමාණවත් තොරතුරු නොමැති විය හැක, නමුත් මෙය කොටස් කිරීමේ යාන්ත්‍රණය භාවිතයෙන් නිවැරදි කළ හැක.
  • ඉදිරියට පැවැත්වේ…

මූලාශ්රය: www.habr.com

අදහස් එක් කරන්න