.NET дорои таймери ришта аст ва он аз таймерҳои WinForms/WPF бо он фарқ мекунад, ки коркардкунандаи он дар риштаи аз ҳавз гирифташуда даъват карда мешавад.
System.Threading.Timer
Инчунин як роҳи хеле экзотикии фиристодани вакил барои иҷро ба ришта аз ҳавз мавҷуд аст - усули BeginInvoke.
DelegateInstance.BeginInvoke
Ман мехоҳам ба таври мухтасар дар бораи функсияе таваққуф намоям, ки бисёре аз усулҳои дар боло зикршударо метавон номид - CreateThread аз Kernel32.dll Win32 API. Ба шарофати механизми усулҳои экстернӣ роҳи даъват кардани ин функсия вуҷуд дорад. Ман танҳо як маротиба чунин зангро дар мисоли даҳшатноки рамзи мерос дида будам ва ангезаи муаллифе, ки маҳз ин корро кардааст, то ҳол барои ман як сирр боқӣ мемонад.
Kernel32.dll CreateThread
Намоиш ва ислоҳи риштаҳо
Риштаҳои эҷодкардаи шумо, ҳама ҷузъҳои тарафи сеюм ва ҳавзи .NET-ро дар равзанаи Threads Visual Studio дидан мумкин аст. Ин равзана танҳо ҳангоми ислоҳи барнома ва дар реҷаи Танаффус маълумоти ришта нишон медиҳад. Дар ин ҷо шумо метавонед ба осонӣ номҳои стек ва афзалиятҳои ҳар як риштаро бубинед ва ислоҳи ислоҳро ба риштаи мушаххас гузаронед. Бо истифода аз хосияти Priority-и синфи Thread, шумо метавонед афзалияти риштаро муқаррар кунед, ки онро OC ва CLR ҳангоми тақсими вақти протсессор байни риштаҳо ҳамчун тавсия қабул мекунанд.
async як тағирдиҳандаи усулест, ки супориш ё бекориро бармегардонад
ва интизорӣ як оператори интизории масдудкунанда нест.
Бори дигар: оператори интизорӣ, дар ҳолати умумӣ (истисноҳо вуҷуд доранд) риштаи ҷории иҷроро минбаъд озод мекунад ва вақте ки супориш иҷрои худро ба анҷом мерасонад, ва ришта (воқеан, контекст гуфтан дурусттар мебуд. , аммо бештар дар бораи он баъдтар) иҷрои усулро минбаъд идома медиҳад. Дар дохили .NET, ин механизм ҳамон тавре, ки ҳосили бозгашт амалӣ карда мешавад, вақте усули хаттӣ ба як синф табдил меёбад, ки мошини давлатӣ аст ва вобаста ба ин ҳолат метавонад дар қисмҳои алоҳида иҷро карда шавад. Ҳар як хоҳишманд метавонад бо истифода аз asyns/await ҳама гуна коди оддиро нависад, бо истифода аз JetBrains dotPeek бо фаъолсозии Рамзи тавлидшудаи Compiler маҷлисро тартиб диҳад ва бубинад.
Биёед имконоти оғоз ва истифодаи Вазифаро дида бароем. Дар мисоли коди дар поён овардашуда, мо вазифаи наверо эҷод мекунем, ки ҳеҷ чизи фоиданок намекунад (Thread.Sleep (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 як ишораи он аст, ки супориш ба зудӣ иҷро нахоҳад шуд, яъне маънои онро дорад, ки ба назар гирифтан лозим нест, ки ришта аз ҳавз нагиред, балки барои ин вазифа алоҳида эҷод кунед, то ба дигарон зарар нарасонад.
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
}
Ин танҳо як мисол аст ва гумон аст, ки шумо ин корро бо намудҳои дарунсохт иҷро кунед, аммо ҳама лоиҳаи кӯҳна танҳо бо усулҳои BeginDoSomething пур аст, ки усулҳои IAsyncResult ва EndDoSomething-ро бармегардонанд, ки онро қабул мекунанд.
Бо истифода аз синфи TaskCompletionSource API-и кӯҳнаро ба Task Based табдил диҳед
Боз як воситаи муҳиме, ки бояд баррасӣ шавад, синф аст Сарчашмаи анҷоми супориш. Аз нуқтаи назари функсияҳо, мақсад ва принсипи кор, он метавонад то андозае усули RegisterWaitForSingleObject синфи ThreadPool-ро ба хотир орад, ки ман дар бораи он дар боло навишта будам. Бо истифода аз ин синф, шумо метавонед ба осонӣ ва ба осонӣ API-ҳои асинхронии кӯҳнаро дар Tasks печонед.
TaskCompletionSource барои печонидани Вазифаҳо ва API-ҳои кӯҳна, ки дар атрофи модели рӯйдод сохта шудаанд, комил аст. Мохияти кори он аз ин иборат аст: объекти ин класс дорои моликияти умумии типи Task мебошад, ки холати онро тавассути усулхои SetResult, SetException ва г. класси TaskCompletionSource идора кардан мумкин аст. Дар ҷойҳое, ки оператори интизорӣ ба ин Вазифа истифода шудааст, он вобаста ба усули ба TaskCompletionSource татбиқшаванда ба истиснои ҳолат иҷро мешавад ё ноком мешавад. Агар он то ҳол равшан набошад, биёед ин мисоли кодро бубинем, ки дар он баъзе API-и кӯҳнаи EAP дар супориш бо истифода аз TaskCompletionSource печонида шудааст: вақте ки ҳодиса сар мезанад, супориш ба ҳолати анҷомёфта интиқол дода мешавад ва усуле, ки оператори интизориро истифода кардааст ба ин вазифадо баъди гирифтани объектдо ичрои онро давом медидад натиҷа.
public static Task<Result> DoAsync(this SomeApiInstance someApiObj) {
var completionSource = new TaskCompletionSource<Result>();
someApiObj.Done +=
result => completionSource.SetResult(result);
someApiObj.Do();
result completionSource.Task;
}
Маслиҳатҳо ва ҳилаҳо
Пӯшидани API-ҳои кӯҳна на ҳама чизест, ки бо истифода аз TaskCompletionSource анҷом дода мешавад. Истифодаи ин синф имкони ҷолиби тарҳрезии API-ҳои гуногунро дар вазифаҳое, ки риштаҳоро ишғол намекунанд, мекушояд. Ва ҷараён, тавре ки мо дар ёд дорем, як манбаи гаронбаҳост ва шумораи онҳо маҳдуд аст (асосан бо миқдори RAM). Ин маҳдудиятро тавассути таҳияи, масалан, як веб-барномаи пурбор бо мантиқи мураккаби тиҷорат ба осонӣ ба даст овардан мумкин аст. Биёед, имко-ниятхоеро, ки ман дар бораи он сухан меронам, хангоми ба амал баровардани чунин найранг, монанди «Поллинги Long-Pulling» ба назар гирем.
Хулоса, моҳияти ҳилла аз ин иборат аст: шумо бояд аз API дар бораи баъзе воқеаҳое, ки дар паҳлӯи он рух медиҳанд, маълумот гиред, дар ҳоле ки API бо баъзе сабабҳо дар бораи ҳодиса хабар дода наметавонад, балки танҳо ҳолати онро баргардонад. Намунаи ин ҳама APIҳо мебошанд, ки дар болои HTTP пеш аз замони WebSocket сохта шудаанд ё вақте ки бо ягон сабаб истифодаи ин технология имконнопазир буд. Мизоҷ метавонад аз сервери HTTP пурсад. Сервери HTTP худаш алоқаро бо муштарӣ оғоз карда наметавонад. Як ҳалли оддӣ пурсиши сервер бо истифода аз таймер аст, аммо ин як бори иловагӣ дар сервер ва таъхири иловагиро ба ҳисоби миёна TimerInterval / 2 эҷод мекунад. Барои бартараф кардани ин ҳиллае бо номи Long Polling ихтироъ карда шуд, ки он таъхири посухро дар бар мегирад. сервер то ба охир расидани мӯҳлат ё ҳодиса рӯй диҳад. Агар ҳодиса рух дода бошад, пас он коркард карда мешавад, агар не, дархост дубора фиристода мешавад.
Аммо чунин ҳалли даҳшатнок ба зудӣ собит хоҳад шуд, ки шумораи муштариёни интизори чорабинӣ зиёд мешавад, зеро... Ҳар як муштарӣ як риштаи пурраро ишғол мекунад, ки интизори ҳодиса аст. Бале, ва вақте ки ин ҳодиса оғоз мешавад, мо 1ms таъхири иловагӣ мегирем, аксар вақт ин аҳамият надорад, аммо чаро нармафзорро аз он метавонад бадтар кунад? Агар мо Thread.Sleep(1)-ро хориҷ кунем, он гоҳ беҳуда як ядрои протсессорро 100% бекор, дар як давраи бефоида давр мезанем. Бо истифода аз 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);
}
}
Вақте ки мо дархост барои паём мегирем, мо TaskCompletionSource-ро дар луғат эҷод мекунем ва ҷойгир мекунем ва он гоҳ интизор мешавем, ки аввал чӣ мешавад: фосилаи вақти муайяншуда тамом мешавад ё паём қабул мешавад.
ValueTask: чаро ва чӣ тавр
Операторҳои асинх/интизорӣ, ба монанди оператори баргардонидани ҳосил, аз метод як мошини ҳолатиро тавлид мекунанд ва ин эҷоди объекти нав аст, ки қариб ҳамеша муҳим нест, аммо дар ҳолатҳои кам он метавонад мушкилот эҷод кунад. Ин ҳолат метавонад як усуле бошад, ки воқеан зуд-зуд даъват карда мешавад, сухан дар бораи даҳҳо ва садҳо ҳазорҳо дар як сония меравад. Агар чунин усул тавре навишта шуда бошад, ки дар аксари мавридҳо он натиҷаро пас аз ҳамаи усулҳои интизорӣ баргардонад, пас .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#, дар ин ҳолат мисли вазифаи муқаррарӣ рафтор хоҳад кард.
LimitedConcurrencyLevelTaskScheduler — шумораи Вазифаҳои дар як вақт иҷрошавандаро аз рӯи параметри N, ки дар конструктор қабул карда мешавад, маҳдуд мекунад
Шумо бояд асбобҳои кор бо риштаҳо, асинхронӣ ва параллелизмро донед, то аз захираҳои компютерҳои компютерии ҳозиразамон истифода баред.
.NET барои ин мақсадҳо асбобҳои гуногун дорад
На ҳамаи онҳо якбора пайдо шуданд, аз ин рӯ шумо аксар вақт чизҳои кӯҳнаро пайдо карда метавонед, аммо роҳҳои табдил додани API-ҳои кӯҳна бе кӯшиши зиёд мавҷуданд.
Кор бо риштаҳо дар .NET бо синфҳои Thread ва ThreadPool муаррифӣ мешавад
Усулҳои Thread.Abort, Thread.Interrupt ва Win32 API TerminateThread хатарноканд ва барои истифода тавсия дода намешаванд. Ба ҷои ин, беҳтар аст, ки механизми CancellationToken -ро истифода баред
Ҷараён захираи арзишманд аст ва таъминоти он маҳдуд аст. Аз ҳолатҳое, ки риштаҳо бо интизории рӯйдодҳо банд ҳастанд, бояд пешгирӣ карда шавад. Барои ин истифодаи синфи TaskCompletionSource қулай аст
Воситаҳои пурқувват ва пешрафтаи .NET барои кор бо параллелизм ва асинхронӣ Вазифаҳо мебошанд.
Равзанаҳои Visual Studio Tasks and Threads маълумоти зиёдеро барои ислоҳи коди бисёр ришта ё асинхронӣ муфид медиҳанд
PLinq як воситаи олиҷаноб аст, аммо он метавонад дар бораи манбаи маълумоти шумо маълумоти кофӣ надошта бошад, аммо онро бо истифода аз механизми тақсимкунӣ ислоҳ кардан мумкин аст.