.NET: د ملټي ریډینګ او ایسینکروني سره د کار کولو وسیلې. برخه 1

زه اصلي مقاله په حبر کې خپروم، چې ژباړه یې په کارپوریټ کې خپره شوې ده د بلاګ پوسټ.

دلته او اوس پایلې ته د انتظار پرته ، یا د ډیری واحدونو تر مینځ د لوی کار ویشلو اړتیا په غیر متناسب ډول د کمپیوټر له راتګ دمخه شتون درلود. د دوی په راتګ سره، دا اړتیا خورا جدي شوه. اوس ، په 2019 کې ، زه دا مقاله په لپ ټاپ کې د 8 کور انټیل کور پروسیسر سره ټایپ کوم ، په کوم کې چې له سلو څخه ډیر پروسې موازي پرمخ ځي ، او حتی نور تارونه. نږدې ، یو څه شنډ تلیفون شتون لري ، چې څو کاله دمخه پیرودل شوی و ، دا په تخته کې د 8 کور پروسیسر لري. موضوعي سرچینې د مقالو او ویډیوګانو څخه ډکې دي چیرې چې د دوی لیکوالان د دې کال پرچم بردار سمارټ فونونه ستاینه کوي چې د 16 کور پروسیسرونه لري. MS Azure یو مجازی ماشین د 20 کور پروسیسر او 128 TB رام سره د $ 2 / ساعت څخه لږ لپاره چمتو کوي. له بده مرغه، دا ناشونې ده چې اعظمي استخراج او د دې ځواک کارول پرته له دې چې د تارونو تعامل اداره کړي.

اصطلاحات

پروسه - د OS څیز، جلا پته ځای، تارونه لري.
موضوع - یو OS اعتراض، د اجرا کولو ترټولو کوچنی واحد، د پروسې برخه، تارونه په یوه پروسه کې په خپل منځ کې حافظه او نورې سرچینې شریکوي.
ملټي ټاسکینګ - د OS ملکیت، په یو وخت کې د څو پروسو چلولو وړتیا
ملټي کور - د پروسیسر ملکیت، د معلوماتو پروسس کولو لپاره د څو کورونو کارولو وړتیا
ملټي پروسس کول - د کمپیوټر ملکیت، د فزیکي پلوه د څو پروسیسرونو سره په ورته وخت کې د کار کولو وړتیا
څو اړخیزه - د پروسې ملکیت، د څو تارونو ترمنځ د معلوماتو پروسس کولو توزیع کولو وړتیا.
موازي - د وخت په هر واحد کې په فزیکي توګه په ورته وخت کې د څو کړنو ترسره کول
همیشه - د دې پروسې بشپړیدو ته انتظار کولو پرته د عملیاتو اجرا کول؛ د اعدام پایله وروسته پروسس کیدی شي.

استعاره

ټول تعریفونه ښه ندي او ځینې اضافي توضیحاتو ته اړتیا لري، نو زه به په رسمي توګه معرفي شوي اصطلاحاتو ته د ناري پخولو په اړه یو استعاره اضافه کړم. په دې استعاره کې د ناستي پخول یو عمل دی.

د سهار د ناشتې د چمتو کولو پر مهال زه (سی پی یوزه پخلنځي ته راځم (کمپیوټر). زه دوه لاسونه لرم (کورونه). په پخلنځي کې یو شمیر وسایل شتون لري (IO): تنور، کیتلی، ټوسټر، یخچال. زه ګاز چالان کړم، په هغې کې د یخولو پین کېږدم او په هغې کې تیل واچوم پرته له دې چې د تودوخې انتظار وکړئ (په غیر متناسب ډول، غیر بندول-IO-انتظار)، زه هګۍ له یخچال څخه واخلم او په یوه پلیټ کې یې مات کړم، بیا یې په یوه لاس ووهلم (موضوع # 1)، او دوهم(موضوع # 2) پلیټ ساتل (شریک سرچینې). اوس زه غواړم کیتلی چالان کړم، مګر زه کافي لاسونه نلرم (تنده لوږه) د دې وخت په جریان کې، د پخلی کولو پین ګرمیږي (د پایلو پروسس کول) کوم چې زه په هغه څه کې اچوم چې ما څټ کړي دي. زه کیتلی ته لاس اچوم او هغه یې وتړم او په احمقانه توګه په هغې کې د اوبو جوش ګورم (بندول-IO-انتظار)، که څه هم د دې وخت په جریان کې هغه کولی شي هغه پلیټ مینځل کړي چیرې چې هغه آملیٹ وهل.

ما یوازې د 2 لاسونو په کارولو سره آملیټ پخ کړ، او زه نور نلرم، مګر په ورته وخت کې، د آملیٹ د وهلو په وخت کې، په یو وخت کې 3 عملیات ترسره شول: د آمیلټ څکول، د پلیټ مینځل، د وریجو ګرمول CPU د کمپیوټر تر ټولو چټکه برخه ده، IO هغه څه دي چې ډیری وختونه هر څه ورو کوي، نو ډیری وختونه یو اغیزمن حل دا دی چې د IO څخه ډاټا ترلاسه کولو په وخت کې د CPU سره یو څه ونیسي.

د استعاره دوام:

  • که د آملیټ چمتو کولو په جریان کې، زه به هڅه وکړم چې جامې بدل کړم، دا به د څو کارونو یوه بیلګه وي. یو مهم ټکی: کمپیوټر په دې کې د خلکو په پرتله خورا ښه دي.
  • یو پخلنځی د څو شیفونو سره، د بیلګې په توګه په رستورانت کې - یو څو کور کمپیوټر.
  • ډیری رستورانتونه د خوراکي توکو په محکمه کې د پیرودلو په مرکز کې - د معلوماتو مرکز

.NET اوزار

.NET د نورو شیانو په څیر د تارونو سره کار کولو کې ښه دی. د هرې نوې نسخې سره ، دا د دوی سره د کار کولو لپاره ډیر او ډیر نوي اوزار معرفي کوي ، د OS تارونو په اړه د خلاصون نوي پرتونه. کله چې د خلاصون جوړولو سره کار کوي، د چوکاټ پراختیا کونکي داسې طریقه کاروي چې فرصت پریږدي، کله چې د لوړې کچې خلاصون کاروي، د یوې یا ډیرو کچو لاندې ته ځي. ډیری وختونه دا اړین ندي ، په حقیقت کې دا د شاټ ټوپک سره د ځان په پښو کې د ډزو کولو دروازه پرانیزي ، مګر ځینې وختونه ، په نادره مواردو کې ، دا ممکن د یوې ستونزې حل کولو یوازینۍ لار وي چې د خلاصون په اوسني کچه کې نه حل کیږي. .

د وسیلو په واسطه ، زما مطلب دواړه د غوښتنلیک برنامې انٹرفیسونه (APIs) دي چې د چوکاټ او دریمې ډلې کڅوړو لخوا چمتو شوي ، په بیله بیا د سافټویر بشپړ حلونه چې د څو کوډ کوډ پورې اړوند هرې ستونزې لپاره لټون اسانه کوي.

د یوې موضوع پیل کول

د موضوع ټولګي د تارونو سره د کار کولو لپاره په .NET کې ترټولو لومړني ټولګي دي. جوړونکی د دوو استازو څخه یو مني:

  • ThreadStart - هیڅ پیرامیټونه نشته
  • ParametrizedThreadStart - د ډول اعتراض د یو پیرامیټر سره.

استازی به د سټارټ میتود له زنګ وهلو وروسته په نوي جوړ شوي تار کې اجرا شي. که چیرې د پارامیټریډ ټریډ سټارټ ډول استازی جوړونکي ته لیږدول شوی وي نو یو اعتراض باید د سټارټ میتود ته انتقال شي. دې میکانیزم ته اړتیا ده چې هر ډول محلي معلومات جریان ته انتقال کړي. دا د یادونې وړ ده چې د تار رامینځته کول یو ګران کار دی ، او تار پخپله یو دروند څیز دی ، لږترلږه ځکه چې دا په سټیک کې د 1MB حافظه تخصیص کوي او د OS API سره تعامل ته اړتیا لري.

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

د ThreadPool ټولګي د حوض مفهوم څرګندوي. په .NET کې، د تار حوض د انجینرۍ یوه برخه ده، او په مایکروسافټ کې پراختیا کونکو ډیرې هڅې کړې ترڅو ډاډ ترلاسه کړي چې دا په مختلفو سناریوګانو کې په ښه توګه کار کوي.

عمومي مفهوم:

له هغه شیبې څخه چې غوښتنلیک پیل کیږي ، دا په شالید کې په زیرمه کې ډیری تارونه رامینځته کوي او د کارولو لپاره یې د اخیستو وړتیا چمتو کوي. که تارونه په مکرر ډول او په لوی شمیر کې وکارول شي ، حوض پراخیږي ترڅو د زنګ وهونکي اړتیاوې پوره کړي. کله چې په سم وخت کې په حوض کې وړیا تارونه شتون ونلري، نو دا به یا د یو تار بیرته راستنیدو ته انتظار وکړي، یا یو نوی جوړ کړي. دا تعقیبوي چې د تار حوض د ځینې لنډ مهاله کړنو لپاره عالي دی او د عملیاتو لپاره ضعیف مناسب دی چې د غوښتنلیک په ټوله عملیاتو کې د خدماتو په توګه پرمخ ځي.

د حوض څخه د تار کارولو لپاره، د QueueUserWorkItem میتود شتون لري چې د WaitCallback ډول استازی مني، کوم چې د ParametrizedThreadStart په څیر ورته لاسلیک لري، او پیرامیټر ورته ورته فعالیت ترسره کوي.

ThreadPool.QueueUserWorkItem(...);

د لږ پیژندل شوي تار پول میتود RegisterWaitForSingleObject د غیر بلاک کولو IO عملیاتو تنظیم کولو لپاره کارول کیږي. دې میتود ته لیږل شوی استازی به ویل کیږي کله چې د WaitHandle میتود ته لیږدول شوی "ریلیز شوی" وي.

ThreadPool.RegisterWaitForSingleObject(...)

.NET د تار ټایمر لري او دا د WinForms/WPF ټایمرونو څخه توپیر لري په دې کې چې د هغې هینډلر به د حوض څخه اخیستل شوي تار ته ویل کیږي.

System.Threading.Timer

د حوض څخه تار ته د اجرا کولو لپاره د استازی لیږلو لپاره یوه بهرنۍ لاره هم شتون لري - د پیل پیل کولو میتود.

DelegateInstance.BeginInvoke

زه غواړم په لنډه توګه د هغه فنکشن په اړه فکر وکړم چې پورته ډیری میتودونه ورته ویل کیدی شي - د Kernel32.dll Win32 API څخه CreateThread. یوه لاره شتون لري، د بهرنیو میتودونو میکانیزم څخه مننه، د دې فنکشن غوښتنه کول. ما دا ډول زنګ یوازې یو ځل د میراث کوډ په ناوړه مثال کې لیدلی دی، او د لیکوال هڅونه چې دا یې په سمه توګه ترسره کړې لاهم زما لپاره یو راز پاتې دی.

Kernel32.dll CreateThread

د تارونو لیدل او ډیبګ کول

ستاسو لخوا جوړ شوي تارونه، د دریمې ډلې ټولې برخې، او د .NET حوض د بصری سټوډیو په کړکۍ کې لیدل کیدی شي. دا کړکۍ به یوازې د تار معلومات ښکاره کړي کله چې غوښتنلیک د ډیبګ او بریک حالت کې وي. دلته تاسو کولی شئ په اسانۍ سره د هرې تار نومونه او لومړیتوبونه وګورئ، او یو ځانګړي تار ته د ډیبګ کولو بدل کړئ. د موضوع ټولګي د لومړیتوب ملکیت په کارولو سره ، تاسو کولی شئ د تار لومړیتوب وټاکئ ، کوم چې OC او CLR به د سپارښتنې په توګه درک کړي کله چې د تارونو ترمینځ د پروسیسر وخت ویشل کیږي.

.NET: د ملټي ریډینګ او ایسینکروني سره د کار کولو وسیلې. برخه 1

کار موازي کتابتون

ټاسک موازي کتابتون (TPL) په .NET 4.0 کې معرفي شو. اوس دا د اسینکروني سره کار کولو لپاره معیاري او اصلي وسیله ده. هر هغه کوډ چې پخوانۍ طریقه کاروي میراث ګڼل کیږي. د TPL بنسټیز واحد د System.Threading.Tasks نوم ځای څخه د کاري ټولګي دی. دنده د تار په اړه یو خلاصون دی. د C# ژبې د نوې نسخې سره، موږ د Tasks - async/await operators سره کار کولو لپاره په زړه پورې لاره ترلاسه کړه. دې مفاهیمو د غیر متناسب کوډ لیکلو امکان رامینځته کړی لکه څنګه چې دا ساده او همغږي وي ، دې کار حتی د هغو خلکو لپاره هم ممکن کړی چې د تارونو داخلي کارونو لږ پوهه لري هغه غوښتنلیکونه ولیکي چې دوی یې کاروي ، هغه غوښتنلیکونه چې د اوږدې عملیاتو ترسره کولو پرمهال کنګل نه کیږي. د async/await کارول د یوې یا حتی څو مقالو لپاره یوه موضوع ده، مګر زه به هڅه وکړم چې په څو جملو کې د هغې لنډیز ترلاسه کړم:

  • async د ټاسک یا باطل بیرته راستنیدو میتود بدلونکی دی
  • او انتظار یو نه بلاکیدونکي کاري انتظار چلونکی دی.

یوځل بیا: د انتظار چلونکی ، په عمومي حالت کې (استثنا شتون لري) به د اجرا کولو اوسنی تار نور هم خوشې کړي ، او کله چې ټاسک خپل اجرا پای ته ورسوي ، او تار (په حقیقت کې ، دا به ډیر سم وي چې شرایط ووایاست. ، مګر په دې اړه نور وروسته) به د میتود اجرا کولو ته دوام ورکړي. د .NET دننه، دا میکانیزم د حاصلاتو بیرته ستنیدو په څیر پلي کیږي، کله چې لیکل شوي میتود په ټول ټولګي بدل شي، کوم چې یو دولتي ماشین دی او د دې ریاستونو پورې اړه لري په جلا جلا برخو کې اجرا کیدی شي. هرڅوک چې علاقه لري کولی شي د asynс/await په کارولو سره کوم ساده کوډ ولیکي ، د 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 باطل په توګه نښه شوی ، پدې معنی چې دا کولی شي د انتظار آپریټر وکاروي ، مګر د زنګ وهلو کوډ به د اجرا کیدو انتظار نشي کولی. که دا ډول ځانګړتیا اړینه وي، نو بیا طریقه باید بیرته راستانه شي. د async باطل په نښه شوي میتودونه خورا عام دي: د یوې قاعدې په توګه ، دا د پیښې اداره کونکي یا نور میتودونه دي چې په اور باندې کار کوي او اصول هیروي. که تاسو اړتیا لرئ نه یوازې دا فرصت ورکړئ چې د اعدام تر پای پورې انتظار وکړئ ، بلکه پایله یې هم بیرته ورکړئ ، نو تاسو اړتیا لرئ ټاسک وکاروئ.

په هغه دنده کې چې د 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 کارول خورا اسانه دي - دوی ورته چلند کوي لکه څنګه چې کوډ همغږي وي. په داسې حال کې چې که موږ په یوه دنده کې همغږي انتظار exorcism پلي کړو، اصلي استثنا په AggregateException بدلیږي، د بیلګې په توګه. د استثنا د اداره کولو لپاره، تاسو باید د InnerException ډول معاینه کړئ او د کیچ بلاکونو د سلسلې پرځای چې په C# نړۍ کې ډیر پیژندل شوي د یو کیچ بلاک دننه د if chain ځان ته ولیکئ یا د جوړیدو پرمهال کیچ وکاروئ.

دریم او وروستنی مثالونه هم د ورته دلیل لپاره خراب نښه شوي او ټول ورته ستونزې لري.

کله چې کوم او کله ټول میتودونه د دندو یوې ډلې ته د انتظار کولو لپاره خورا اسانه دي؛ دوی د دندو یوه ډله په یوه کې پوښي ، کوم چې یا به کله چې د ډلې څخه یو کار لومړی پیل شي ، یا کله چې دوی ټول خپل اجرا بشپړ کړي.

د تارونو بندول

د مختلفو دلیلونو لپاره، دا ممکن اړین وي چې د جریان پیل کولو وروسته ودرول شي. د دې کولو لپاره یو شمیر لارې شتون لري. د موضوع ټولګي دوه مناسب نومول شوي میتودونه لري: بندول и مداخله. لومړی د کارولو لپاره خورا سپارښتنه نه کیږي، ځکه چې په هره تصادفي شیبه کې د زنګ وهلو وروسته ، د هرې لارښوونې پروسس کولو پرمهال ، یو استثنا به وغورځول شي ThreadAbortedException. تاسو تمه نه لرئ چې دا ډول استثنا وغورځول شي کله چې کوم عدد متغیر زیات کړئ، سمه ده؟ او کله چې دا طریقه کاروئ، دا یو خورا ریښتینی حالت دی. که تاسو اړتیا لرئ د کوډ په یوې ټاکلې برخه کې د داسې استثناء رامینځته کولو څخه د CLR مخه ونیسئ ، تاسو کولی شئ دا په تلیفونونو کې وتړئ Thread.BeginCriticalRegion, Thread.EndCriticalRegion. کوم کوډ چې په نهایت کې په بلاک کې لیکل شوی په ورته تلیفونونو کې پوښل شوی. د دې دلیل لپاره ، د چوکاټ کوډ ژورو کې تاسو کولی شئ د خالي هڅې سره بلاکونه ومومئ ، مګر په پای کې خالي نه. مایکروسافټ دا میتود دومره هڅوي چې دوی یې په .net کور کې شامل نه کړل.

د مداخلې طریقه په ډیر اټکل سره کار کوي. دا کولی شي د استثنا سره تار مداخله وکړي ThreadInterruptedException یوازې د هغه شیبو په جریان کې کله چې تار د انتظار په حالت کې وي. دا دې حالت ته ننوځي پداسې حال کې چې ځړول کیږي د انتظار په وخت کې د WaitHandle، تالاشي، یا د Thread.Sleep زنګ وهلو وروسته.

پورته بیان شوي دواړه اختیارونه د دوی د نه اټکل کیدو له امله خراب دي. د حل لاره د جوړښت کارول دي د لغوه کولو ټوکن او ټولګي د لغوه کولو ټوکن سرچینه. نقطه دا ده: د CancellationTokenSource ټولګي یوه بیلګه رامینځته شوې او یوازې هغه څوک چې دا یې لري کولی شي د میتود په زنګ وهلو سره عملیات ودروي لغوه کړه. یوازې د منسوخ کولو ټوکن پخپله عملیاتو ته لیږدول کیږي. د منسوخ کولو ټکن مالکین نشي کولی پخپله عملیات لغوه کړي ، مګر یوازې کولی شي وګوري چې ایا عملیات لغوه شوي که نه. د دې لپاره د بولین ملکیت شتون لري د لغوه کولو غوښتنه شوې او طریقه ThrowIfCancelRequested. وروستی به یو استثنا وغورځوي TaskCancelledException که چیرې د فسخ کولو طریقه د CancellationToken په مثال کې ویل شوې وي توطیه شوې. او دا هغه میتود دی چې زه یې کارولو وړاندیز کوم. دا د پخوانیو اختیارونو په پرتله یو ښه والی دی چې د بشپړ کنټرول ترلاسه کولو سره په کوم ځای کې استثنایی عملیات لغوه کیدی شي.

د تار بندولو لپاره ترټولو ظالمانه اختیار د Win32 API TerminateThread فنکشن ته زنګ وهل دي. د دې فنکشن زنګ وهلو وروسته د CLR چلند ممکن غیر متوقع وي. په MSDN کې د دې فعالیت په اړه لاندې لیکل شوي: "TerminateThread یو خطرناک فعالیت دی چې یوازې په خورا سختو قضیو کې باید وکارول شي. "

د FromAsync میتود په کارولو سره د ټاسک پراساس د میراث API بدلول

که تاسو دومره خوشحاله یاست چې په یوه پروژه کې کار وکړئ چې د ټاسک معرفي کیدو وروسته پیل شوی و او د ډیری پراختیا کونکو لپاره د خاموش وحشت لامل شوی و ، نو تاسو به د ډیری پخوانیو APIs سره معامله ونه کړئ ، دواړه دریمې ډلې او هغه ستاسو ټیم. په تیرو وختونو کې شکنجه شوي. خوشبختانه، د .NET چوکاټ ټیم زموږ پاملرنه وکړه، که څه هم شاید هدف د خپل ځان ساتنه وه. لکه څنګه چې کیدی شي، .NET یو شمیر وسیلې لري چې په زړو غیر متناسب پروګرامونو کې لیکل شوي کوډونه په بې درده توګه بدلوي چې نوي ته. یو له دوی څخه د TaskFactory FromAsync میتود دی. لاندې د کوډ مثال کې ، زه د دې میتود په کارولو سره د WebRequest ټولګي زاړه async میتودونه په ټاسک کې وتړم.

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

دا یوازې یو مثال دی او تاسو احتمال نه لرئ چې دا د جوړ شوي ډولونو سره ترسره کړئ ، مګر کومه پخوانۍ پروژه په ساده ډول د BeginDoSomething میتودونو سره یوځای کیږي چې د IAsyncResult او EndDoSomething میتودونه بیرته راګرځوي چې دا ترلاسه کوي.

د TaskCompletionSource ټولګي په کارولو سره د میراث API Task Based ته واړوئ

د غور کولو لپاره بله مهمه وسیله ټولګي ده د کار بشپړولو سرچینه. د دندو ، هدف او د عملیاتو اصولو په شرایطو کې ، دا ممکن یو څه د ThreadPool ټولګي RegisterWaitForSingleObject میتود یادونه وکړي ، کوم چې ما پورته په اړه لیکلي. د دې ټولګي په کارولو سره، تاسو کولی شئ په اسانۍ او په اسانۍ سره په کارونو کې زاړه غیر مطابقت لرونکي API وتړئ.

تاسو به ووایاست چې ما دمخه د دې موخو لپاره د ټاسک فاکتوري ټولګي د FromAsync میتود په اړه خبرې کړې دي. دلته به موږ په .net کې د اسینکرونوس ماډلونو د پراختیا ټول تاریخ په یاد ولرو چې مایکروسافټ په تیرو 15 کلونو کې وړاندیز کړی: د ټاسک پراساس اسینکرونوس نمونه (TAP) دمخه ، د اسینکرونوس برنامه کولو نمونه (APP) شتون درلود. د میتودونو په اړه وو پيل كيدل؛ شروع كيدل: او چنېدل، راوتلیو څه بیرته راګرځي IAsyncResult او میتودونه پايانDoSomething چې دا مني او د دې کلونو میراث لپاره د FromAsync میتود یوازې بشپړ دی ، مګر د وخت په تیریدو سره ، دا د پیښې پراساس اسینکرونوس نمونې لخوا بدل شوی (او AP)، کوم چې انګیرل چې یوه پیښه به راپورته شي کله چې د غیر متمرکز عملیات بشپړ شي.

د TaskCompletionSource د پیښې ماډل په شاوخوا کې جوړ شوي د Tasks او میراث APIs لپاس کولو لپاره مناسب دی. د دې کار جوهر په لاندې ډول دی: د دې ټولګي یو څیز د ټاسک ډول عامه ملکیت لري ، کوم حالت چې د SetResult ، SetException او داسې نورو میتودونو له لارې د TaskCompletionSource ټولګي کنټرول کیدی شي. په هغه ځایونو کې چیرې چې د انتظار چلونکی په دې ټاسک کې پلي شوی و ، دا به په استثنا سره اجرا شي یا ناکام شي د TaskCompletionSource کې پلي شوي میتود پورې اړه لري. که دا لاهم روښانه نه وي ، راځئ چې د دې کوډ مثال وګورو ، چیرې چې ځینې زاړه EAP API د 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;
}

د کار بشپړولو سرچینې لارښوونې او چلونه

د زړو APIs لپاس کول ټول هغه څه ندي چې د TaskCompletionSource په کارولو سره ترسره کیدی شي. د دې ټولګي کارول په دندو کې د مختلف APIs ډیزاین کولو په زړه پوري امکان خلاصوي چې تارونه نه نیسي. او جریان، لکه څنګه چې موږ یادونه کوو، یوه ګرانه سرچینه ده او د دوی شمیر محدود دی (په عمده توګه د RAM اندازه). دا محدودیت په اسانۍ سره د پراختیا له لارې ترلاسه کیدی شي، د بیلګې په توګه، د پیچلي سوداګرۍ منطق سره یو بار شوي ویب غوښتنلیک. اجازه راکړئ هغه امکانات په پام کې ونیسو چې زه یې په اړه خبرې کوم کله چې د اوږدې رایې ورکولو په څیر داسې چال پلي کول.

په لنډه توګه، د چال جوهر دا دی: تاسو اړتیا لرئ چې د API څخه د ځینو پیښو په اړه معلومات ترلاسه کړئ چې د هغې خوا ته پیښیږي، پداسې حال کې چې API د ځینو دلیلونو لپاره د پیښې راپور نشي کولی، مګر یوازې حالت بیرته کولی شي. د دې یوه بیلګه ټول APIs د WebSocket وختونو دمخه د HTTP په سر کې جوړ شوي یا کله چې د دې ټیکنالوژۍ کارولو لپاره د کوم دلیل لپاره ناممکن و. پیرودونکي کولی شي د HTTP سرور څخه پوښتنه وکړي. د HTTP سرور پخپله نشي کولی د پیرودونکي سره اړیکه پیل کړي. یو ساده حل دا دی چې د ټایمر په کارولو سره سرور رایه اچونه وکړي، مګر دا په سرور کې اضافي بار رامینځته کوي او په اوسط TimerInterval / 2 کې اضافي ځنډ رامینځته کوي. د دې شاوخوا ترلاسه کولو لپاره ، د لانګ پولینګ په نوم یو چل ایجاد شو چې پکې د ځواب ځنډول شامل دي. سرور تر هغه وخته پورې چې د وخت پای پای ته ونه رسیږي یا پیښه رامنځته شي. که پیښه رامنځ ته شي، دا پروسس کیږي؛ که نه، غوښتنه بیا لیږل کیږي.

while(!eventOccures && !timeoutExceeded)  {

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

مګر دا ډول حل به ډیر ژر وژونکي ثابت شي ځکه چې د پیښې په تمه د پیرودونکو شمیر ډیریږي ، ځکه چې ... هر دا ډول پیرودونکی د پیښې په انتظار کې ټوله تار نیسي. هو، او موږ د 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);
    }
}

دا کوډ د تولید لپاره چمتو ندی، مګر یوازې یو ډیمو. په ریښتیني قضیو کې د دې کارولو لپاره ، تاسو اړتیا لرئ لږترلږه د وضعیت اداره کولو لپاره کله چې یو پیغام په داسې وخت کې راشي کله چې هیڅوک یې تمه نه کوي: پدې حالت کې ، د AsseptMessageAsync میتود باید دمخه بشپړ شوی دنده بیرته راستانه کړي. که دا خورا عام قضیه وي، نو تاسو کولی شئ د ValueTask کارولو په اړه فکر وکړئ.

کله چې موږ د پیغام لپاره غوښتنه ترلاسه کوو، موږ په لغت کې د TaskCompletionSource جوړوو او ځای په ځای کوو، او بیا انتظار کوو چې لومړی څه پیښیږي: د ټاکل شوي وخت وقفه پای ته رسیږي یا پیغام ترلاسه کیږي.

ValueTask: ولې او څنګه

د async/await آپریټرونه لکه د حاصل بیرته راستنیدونکي آپریټر، د میتود څخه یو دولتي ماشین رامینځته کوي، او دا د نوي څیز رامینځته کول دي، کوم چې تقریبا تل مهم نه وي، مګر په نادره مواردو کې دا ستونزه رامینځته کولی شي. دا قضیه ممکن یو میتود وي چې واقعیا ډیری وختونه ویل کیږي ، موږ په هره ثانیه کې د لسګونو او سلګونو زرو تلیفونونو په اړه خبرې کوو. که دا ډول میتود په داسې ډول لیکل شوی وي چې په ډیری حاالتو کې دا د انتظار ټولو میتودونو په تیریدو سره پایله بیرته راوړي ، نو .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# نحو له نظره، په دې قضیه کې به د عادي دندې په څیر چلند وکړي.

د کاري مهال ویش: د کار پیل کولو ستراتیژیو اداره کول

راتلونکی API چې زه غواړم په پام کې ونیسم ټولګی دی د کاري مهال ویش او د هغې مشتقات. ما مخکې یادونه وکړه چې TPL د دې وړتیا لري چې د موضوعاتو په اوږدو کې د کارونو ویشلو لپاره ستراتیژۍ اداره کړي. دا ډول ستراتیژۍ د TaskScheduler ټولګي په اولادونو کې تعریف شوي. نږدې هره تګلاره چې تاسو ورته اړتیا لرئ په کتابتون کې موندل کیدی شي. Parallel Extensions Extras، د مایکروسافټ لخوا رامینځته شوی ، مګر د .NET برخه نه ده ، مګر د Nuget کڅوړې په توګه چمتو شوی. راځئ چې په لنډه توګه ځینې یې وګورو:

  • CurrentThreadTaskScheduler - په اوسني تار کې دندې اجرا کوي
  • LimitedConcurrency LevelTask ​​Scheduler - د پیرامیټر N لخوا په ورته وخت کې اجرا شوي دندو شمیر محدودوي ، کوم چې په جوړونکي کې منل کیږي
  • OrderedTaskScheduler - د LimitedConcurrencyLevelTaskScheduler(1) په توګه تعریف شوی، نو دندې به په ترتیب سره اجرا شي.
  • WorkStealingTaskScheduler - تطبیقوي د کار غلا کول د دندو ویشلو ته لاره. په لازمي ډول دا یو جلا ThreadPool دی. دا ستونزه حل کوي چې په .NET ThreadPool کې یو جامد ټولګی دی، د ټولو غوښتنلیکونو لپاره یو، پدې معنی چې د برنامه په یوه برخه کې د هغې ډیر بار یا غلط استعمال کولی شي په بل کې د اړخیزو اغیزو لامل شي. برسېره پر دې، دا خورا ستونزمن کار دی چې د دې ډول نیمګړتیاوو لاملونه پوه شي. هغه. د برنامه په برخو کې ممکن د جلا WorkStealingTaskSchedulers کارولو ته اړتیا وي چیرې چې د ThreadPool کارول ممکن تیریدونکي او غیر متوقع وي.
  • د قطار کاري مهال ویش - تاسو ته اجازه درکوي د لومړیتوب قطار قواعدو سره سم دندې ترسره کړئ
  • ThreadPerTaskScheduler - د هرې دندې لپاره جلا تار رامینځته کوي چې په هغې کې اجرا کیږي. د هغو دندو لپاره ګټور کیدی شي چې بشپړیدو لپاره غیر متوقع اوږد وخت نیسي.

ښه تفصیل لري مقاله په مایکروسافټ بلاګ کې د TaskSchedulers په اړه.

د دندو پورې اړوند هرڅه د اسانه ډیبګ کولو لپاره ، لید سټوډیو د دندو کړکۍ لري. په دې کړکۍ کې تاسو کولی شئ د دندې اوسنی حالت وګورئ او د کوډ اوسني اجرا کوونکې کرښې ته لاړ شئ.

.NET: د ملټي ریډینګ او ایسینکروني سره د کار کولو وسیلې. برخه 1

PLinq او موازي ټولګي

د دندو سربیره او د دوی په اړه هرڅه ویل شوي ، په .NET کې دوه نور په زړه پوري وسیلې شتون لري: PLinq (Linq2Parallel) او موازي ټولګي. لومړی ژمنه کوي چې په څو تارونو کې د لینک ټول عملیات موازي اجرا کوي. د تارونو شمیر د WithDegreeOfParallelism توسیع میتود په کارولو سره تنظیم کیدی شي. بدبختانه ، ډیری وختونه PLinq په خپل ډیفالټ حالت کې ستاسو د ډیټا سرچینې داخلي په اړه کافي معلومات نلري ترڅو د پام وړ سرعت لاسته راوړنې چمتو کړي ، له بلې خوا ، د هڅې لګښت خورا ټیټ دی: تاسو اړتیا لرئ مخکې له دې د AsParallel میتود ته زنګ ووهئ. د لینک میتودونو سلسله او د فعالیت ازموینې پرمخ وړي. برسېره پردې، دا ممکنه ده چې د برخې میکانیزم په کارولو سره ستاسو د معلوماتو سرچینې د نوعیت په اړه PLinq ته اضافي معلومات انتقال کړئ. تاسو کولی شئ نور ولولئ دلته и دلته.

د موازي جامد ټولګي په موازي ډول د Foreach ټولګه له لارې د تکرار کولو میتودونه چمتو کوي، د لوپ لپاره اجرا کول، او په موازي بلنه کې د ډیری استازو اجرا کول. د اوسني تار اجرا کول به تر هغه پورې ودرول شي تر څو چې حسابونه بشپړ شوي نه وي. د تارونو شمیر د وروستي دلیل په توګه د ParallelOptions په تیریدو سره تنظیم کیدی شي. تاسو کولی شئ د اختیارونو په کارولو سره TaskScheduler او CancellationToken هم مشخص کړئ.

موندنو

کله چې ما د خپل راپور د موادو او د هغه معلوماتو پر بنسټ چې ما وروسته د کار په جریان کې راټول کړي د دې مقالې لیکل پیل کړل، ما تمه نه درلوده چې دومره به وي. اوس، کله چې د متن مدیر په کوم کې چې زه دا مقاله ټایپ کوم په بده مرغه ما ته وايي چې 15 پاڼه لاړه، زه به لنډمهاله پایلې لنډیز کړم. نور چلونه، APIs، بصری وسیلې او نیمګړتیاوې به په راتلونکې مقاله کې پوښل شي.

پایلې:

  • تاسو اړتیا لرئ د عصري کمپیوټر سرچینو کارولو لپاره د تارونو ، غیر مطابقت او موازي سره کار کولو لپاره وسیلې وپیژنئ.
  • .NET د دې موخو لپاره ډیری مختلف وسایل لري
  • دا ټول په یوځل نه راڅرګند شوي، نو تاسو کولی شئ ډیری وختونه میراث ومومئ، په هرصورت، د ډیرو هڅو پرته د زړو APIs بدلولو لارې شتون لري.
  • په .NET کې د تارونو سره کار کول د Thread او ThreadPool ټولګیو لخوا نمایش کیږي
  • د Thread.Abort، Thread.Interrupt، او Win32 API TerminateThread میتودونه خطرناک دي او د کارولو لپاره وړاندیز نه کیږي. پرځای یې، دا غوره ده چې د CancellationToken میکانیزم وکاروئ
  • جریان یوه ارزښتناکه سرچینه ده او عرضه یې محدوده ده. هغه حالتونه چیرې چې تارونه د پیښو په انتظار کې بوخت وي باید مخنیوی وشي. د دې لپاره دا د TaskCompletionSource ټولګي کارول اسانه دي
  • د موازي او غیر مطابقت سره د کار کولو لپاره خورا پیاوړي او پرمختللي .NET وسیلې دندې دي.
  • د c# async/await آپریټرونه د غیر بندیدو انتظار مفهوم پلي کوي
  • تاسو کولی شئ د TaskScheduler څخه ترلاسه شوي ټولګیو په کارولو سره د تارونو په اوږدو کې د دندو ویش کنټرول کړئ
  • د ValueTask جوړښت د ګرمو لارو او حافظې ټرافیک په ښه کولو کې ګټور کیدی شي
  • د بصری سټوډیو د کارونو او تارونو کړکۍ ډیری معلومات چمتو کوي چې د څو تارونو یا غیر متناسب کوډ ډیبګ کولو لپاره ګټور وي
  • PLinq یوه ښه وسیله ده، مګر دا ممکن ستاسو د معلوماتو سرچینې په اړه کافي معلومات ونه لري، مګر دا د ویشلو میکانیزم په کارولو سره حل کیدی شي.
  • نور بیا…

سرچینه: www.habr.com

Add a comment