.NET: మల్టీథ్రెడింగ్ మరియు అసమకాలికతో పని చేసే సాధనాలు. 1 వ భాగము

నేను హబ్ర్‌పై అసలు కథనాన్ని ప్రచురిస్తున్నాను, దాని అనువాదం కార్పొరేట్‌లో పోస్ట్ చేయబడింది బ్లాగ్ పోస్ట్.

ఇక్కడ మరియు ఇప్పుడు ఫలితం కోసం వేచి ఉండకుండా, అసమకాలికంగా ఏదైనా చేయవలసిన అవసరం లేదా దానిని నిర్వహిస్తున్న అనేక యూనిట్ల మధ్య పెద్ద పనిని విభజించడం, కంప్యూటర్లు రాకముందే ఉంది. వారి రాకతో, ఈ అవసరం చాలా స్పష్టంగా మారింది. ఇప్పుడు, 2019లో, నేను ఈ కథనాన్ని 8-కోర్ ఇంటెల్ కోర్ ప్రాసెసర్‌తో ల్యాప్‌టాప్‌లో టైప్ చేస్తున్నాను, దానిపై వందకు పైగా ప్రక్రియలు సమాంతరంగా మరియు మరిన్ని థ్రెడ్‌లు నడుస్తున్నాయి. సమీపంలో, కొద్దిగా చిరిగిన ఫోన్ ఉంది, కొన్ని సంవత్సరాల క్రితం కొనుగోలు చేయబడింది, ఇది బోర్డులో 8-కోర్ ప్రాసెసర్‌ను కలిగి ఉంది. థీమాటిక్ వనరులు కథనాలు మరియు వీడియోలతో నిండి ఉన్నాయి, ఇక్కడ వారి రచయితలు 16-కోర్ ప్రాసెసర్‌లను కలిగి ఉన్న ఈ సంవత్సరం ఫ్లాగ్‌షిప్ స్మార్ట్‌ఫోన్‌లను ఆరాధిస్తారు. MS Azure 20 కోర్ ప్రాసెసర్ మరియు 128 TB RAMతో వర్చువల్ మిషన్‌ను గంటకు $2 కంటే తక్కువ ధరకు అందిస్తుంది. దురదృష్టవశాత్తూ, థ్రెడ్‌ల పరస్పర చర్యను నిర్వహించకుండా గరిష్టంగా సంగ్రహించడం మరియు ఈ శక్తిని ఉపయోగించడం అసాధ్యం.

పదజాలం

ప్రక్రియ - OS ఆబ్జెక్ట్, వివిక్త చిరునామా స్థలం, థ్రెడ్‌లను కలిగి ఉంటుంది.
థ్రెడ్ - ఒక OS ఆబ్జెక్ట్, ఎగ్జిక్యూషన్ యొక్క అతి చిన్న యూనిట్, ప్రక్రియలో భాగం, థ్రెడ్‌లు ఒక ప్రక్రియలో మెమరీని మరియు ఇతర వనరులను తమలో తాము పంచుకుంటాయి.
మల్టీ టాస్కింగ్ - OS ఆస్తి, అనేక ప్రక్రియలను ఏకకాలంలో అమలు చేయగల సామర్థ్యం
బహుళ-కోర్ - ప్రాసెసర్ యొక్క ఆస్తి, డేటా ప్రాసెసింగ్ కోసం అనేక కోర్లను ఉపయోగించగల సామర్థ్యం
మల్టీప్రాసెసింగ్ - కంప్యూటర్ యొక్క ఆస్తి, భౌతికంగా అనేక ప్రాసెసర్‌లతో ఏకకాలంలో పని చేసే సామర్థ్యం
మల్టీథ్రెడింగ్ - ప్రక్రియ యొక్క ఆస్తి, అనేక థ్రెడ్‌ల మధ్య డేటా ప్రాసెసింగ్‌ను పంపిణీ చేయగల సామర్థ్యం.
సమాంతరత - యూనిట్ సమయానికి భౌతికంగా ఏకకాలంలో అనేక చర్యలను చేయడం
అసమకాలిక - ఈ ప్రాసెసింగ్ పూర్తయ్యే వరకు వేచి ఉండకుండా ఒక ఆపరేషన్ అమలు చేయడం వలన అమలు ఫలితం తర్వాత ప్రాసెస్ చేయబడుతుంది.

రూపకం

అన్ని నిర్వచనాలు మంచివి కావు మరియు కొన్నింటికి అదనపు వివరణ అవసరం, కాబట్టి నేను అధికారికంగా ప్రవేశపెట్టిన పదజాలానికి అల్పాహారం వండడం గురించి ఒక రూపకాన్ని జోడిస్తాను. ఈ రూపకంలో అల్పాహారం వండడం ఒక ప్రక్రియ.

ఉదయం అల్పాహారం సిద్ధం చేస్తున్నప్పుడు నేను (CPU) నేను వంటగదికి వచ్చాను (కంప్యూటర్) నాకు 2 చేతులు ఉన్నాయి (కోర్ల) వంటగదిలో అనేక పరికరాలు ఉన్నాయి (IO): ఓవెన్, కేటిల్, టోస్టర్, రిఫ్రిజిరేటర్. నేను గ్యాస్‌ను ఆన్ చేసి, దానిపై ఫ్రైయింగ్ పాన్ వేసి, అది వేడెక్కడానికి వేచి ఉండకుండా నూనె పోయాలి (అసమకాలికంగా, నాన్-బ్లాకింగ్-IO-వెయిట్), నేను రిఫ్రిజిరేటర్ నుండి గుడ్లను తీసి వాటిని ప్లేట్‌గా విడగొట్టి, ఆపై వాటిని ఒక చేత్తో కొట్టాను (థ్రెడ్#1), మరియు రెండవ (థ్రెడ్#2) ప్లేట్‌ను పట్టుకోవడం (షేర్డ్ రిసోర్స్). ఇప్పుడు నేను కేటిల్ ఆన్ చేయాలనుకుంటున్నాను, కానీ నాకు తగినంత చేతులు లేవు (థ్రెడ్ ఆకలి) ఈ సమయంలో, వేయించడానికి పాన్ వేడెక్కుతుంది (ఫలితాన్ని ప్రాసెస్ చేయడం) నేను కొరడాతో కొట్టిన వాటిని పోస్తాను. నేను కెటిల్ వద్దకు చేరుకుని దాన్ని ఆన్ చేసి, అందులో నీరు ఉడకబెట్టడాన్ని మూర్ఖంగా చూస్తున్నాను (నిరోధించడం-IO-వెయిట్), అయితే ఈ సమయంలో అతను ఆమ్లెట్ కొట్టిన ప్లేట్‌ను కడుక్కోవచ్చు.

నేను కేవలం 2 చేతులతో ఆమ్లెట్ వండుకున్నాను మరియు నా దగ్గర ఎక్కువ లేదు, కానీ అదే సమయంలో, ఆమ్లెట్ కొట్టే సమయంలో, ఒకేసారి 3 ఆపరేషన్లు జరిగాయి: ఆమ్లెట్ కొట్టడం, ప్లేట్ పట్టుకోవడం, వేయించడానికి పాన్ వేడి చేయడం CPU అనేది కంప్యూటర్‌లో అత్యంత వేగవంతమైన భాగం, IO అనేది చాలా తరచుగా ప్రతిదీ నెమ్మదిస్తుంది, కాబట్టి తరచుగా IO నుండి డేటాను స్వీకరించేటప్పుడు CPUని ఆక్రమించడం సమర్థవంతమైన పరిష్కారం.

రూపకాన్ని కొనసాగించడం:

  • ఆమ్లెట్ తయారుచేసే ప్రక్రియలో, నేను బట్టలు మార్చడానికి కూడా ప్రయత్నిస్తాను, ఇది బహువిధికి ఉదాహరణ. ఒక ముఖ్యమైన స్వల్పభేదాన్ని: కంప్యూటర్లు ఈ విషయంలో వ్యక్తుల కంటే మెరుగ్గా ఉంటాయి.
  • అనేక చెఫ్‌లతో కూడిన వంటగది, ఉదాహరణకు రెస్టారెంట్‌లో - మల్టీ-కోర్ కంప్యూటర్.
  • షాపింగ్ సెంటర్‌లోని ఫుడ్ కోర్ట్‌లో అనేక రెస్టారెంట్లు - డేటా సెంటర్

.NET సాధనాలు

.NET అనేక ఇతర విషయాలతో పాటు థ్రెడ్‌లతో పని చేయడం మంచిది. ప్రతి కొత్త వెర్షన్‌తో, ఇది వారితో పని చేయడానికి మరిన్ని కొత్త సాధనాలను పరిచయం చేస్తుంది, OS థ్రెడ్‌లపై సంగ్రహణ యొక్క కొత్త పొరలు. సంగ్రహణల నిర్మాణంతో పని చేస్తున్నప్పుడు, ఫ్రేమ్‌వర్క్ డెవలపర్‌లు ఒక ఉన్నత-స్థాయి సంగ్రహణను ఉపయోగిస్తున్నప్పుడు, దిగువ ఒకటి లేదా అంతకంటే ఎక్కువ స్థాయిలను తగ్గించే అవకాశాన్ని వదిలివేసే విధానాన్ని ఉపయోగిస్తారు. చాలా తరచుగా ఇది అవసరం లేదు, వాస్తవానికి ఇది షాట్‌గన్‌తో మిమ్మల్ని మీరు కాల్చుకోవడానికి తలుపులు తెరుస్తుంది, కానీ కొన్నిసార్లు, అరుదైన సందర్భాల్లో, ప్రస్తుత నైరూప్యత స్థాయిలో పరిష్కరించబడని సమస్యను పరిష్కరించడానికి ఇది ఏకైక మార్గం. .

సాధనాల ద్వారా, నా ఉద్దేశ్యం ఫ్రేమ్‌వర్క్ మరియు థర్డ్-పార్టీ ప్యాకేజీల ద్వారా అందించబడిన అప్లికేషన్ ప్రోగ్రామింగ్ ఇంటర్‌ఫేస్‌లు (APIలు), అలాగే మల్టీ-థ్రెడ్ కోడ్‌కు సంబంధించిన ఏవైనా సమస్యల కోసం శోధనను సులభతరం చేసే మొత్తం సాఫ్ట్‌వేర్ పరిష్కారాలు.

థ్రెడ్‌ను ప్రారంభించడం

థ్రెడ్ క్లాస్ అనేది థ్రెడ్‌లతో పని చేయడానికి .NETలో అత్యంత ప్రాథమిక తరగతి. కన్స్ట్రక్టర్ ఇద్దరు ప్రతినిధులలో ఒకరిని అంగీకరిస్తారు:

  • ThreadStart — పారామితులు లేవు
  • ParametrizedThreadStart - రకం వస్తువు యొక్క ఒక పరామితితో.

స్టార్ట్ మెథడ్‌కి కాల్ చేసిన తర్వాత డెలిగేట్ కొత్తగా సృష్టించిన థ్రెడ్‌లో అమలు చేయబడుతుంది, ఒకవేళ పారామెట్రిజ్డ్‌థ్రెడ్‌స్టార్ట్ రకం డెలిగేట్ కన్స్ట్రక్టర్‌కు పంపబడితే, ఆబ్జెక్ట్ తప్పనిసరిగా స్టార్ట్ మెథడ్‌కి పంపబడాలి. ఏదైనా స్థానిక సమాచారాన్ని స్ట్రీమ్‌కి బదిలీ చేయడానికి ఈ మెకానిజం అవసరం. థ్రెడ్‌ను సృష్టించడం ఖరీదైన ఆపరేషన్ అని మరియు థ్రెడ్ కూడా భారీ వస్తువు అని గమనించాలి, ఎందుకంటే ఇది స్టాక్‌లో 1MB మెమరీని కేటాయిస్తుంది మరియు OS APIతో పరస్పర చర్య అవసరం.

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

థ్రెడ్‌పూల్ క్లాస్ పూల్ భావనను సూచిస్తుంది. .NETలో, థ్రెడ్ పూల్ అనేది ఇంజినీరింగ్ యొక్క ఒక భాగం, మరియు మైక్రోసాఫ్ట్‌లోని డెవలపర్‌లు అనేక రకాల దృశ్యాలలో ఇది ఉత్తమంగా పనిచేసేలా చూసుకోవడానికి చాలా కృషి చేసారు.

సాధారణ భావన:

అప్లికేషన్ ప్రారంభమైన క్షణం నుండి, ఇది నేపథ్యంలో రిజర్వ్‌లో అనేక థ్రెడ్‌లను సృష్టిస్తుంది మరియు వాటిని ఉపయోగం కోసం తీసుకునే సామర్థ్యాన్ని అందిస్తుంది. థ్రెడ్‌లను తరచుగా మరియు పెద్ద సంఖ్యలో ఉపయోగించినట్లయితే, కాలర్ అవసరాలను తీర్చడానికి పూల్ విస్తరిస్తుంది. సరైన సమయంలో పూల్‌లో ఉచిత థ్రెడ్‌లు లేనప్పుడు, థ్రెడ్‌లలో ఒకటి తిరిగి వచ్చే వరకు వేచి ఉంటుంది లేదా కొత్తదాన్ని సృష్టిస్తుంది. థ్రెడ్ పూల్ కొన్ని స్వల్పకాలిక చర్యలకు గొప్పదని మరియు అప్లికేషన్ యొక్క మొత్తం ఆపరేషన్‌లో సేవలుగా అమలు చేసే కార్యకలాపాలకు సరిగ్గా సరిపోదని ఇది అనుసరిస్తుంది.

పూల్ నుండి థ్రెడ్‌ను ఉపయోగించడానికి, క్యూయూసర్‌వర్క్ ఐటెమ్ పద్ధతి ఉంది, ఇది WaitCallback రకం ప్రతినిధిని అంగీకరిస్తుంది, ఇది ParametrizedThreadStart వలె అదే సంతకాన్ని కలిగి ఉంటుంది మరియు దానికి పంపిన పరామితి అదే పనిని చేస్తుంది.

ThreadPool.QueueUserWorkItem(...);

అంతగా తెలియని థ్రెడ్ పూల్ పద్ధతి RegisterWaitForSingleObject నాన్-బ్లాకింగ్ IO ఆపరేషన్‌లను నిర్వహించడానికి ఉపయోగించబడుతుంది. వెయిట్‌హ్యాండిల్ మెథడ్‌కి పాస్ అయినప్పుడు "విడుదలైంది" అయినప్పుడు ఈ పద్ధతికి పంపబడిన ప్రతినిధిని పిలుస్తారు.

ThreadPool.RegisterWaitForSingleObject(...)

.NETకి థ్రెడ్ టైమర్ ఉంది మరియు ఇది WinForms/WPF టైమర్‌ల నుండి భిన్నంగా ఉంటుంది, దాని హ్యాండ్లర్ పూల్ నుండి తీసిన థ్రెడ్‌పై పిలువబడుతుంది.

System.Threading.Timer

పూల్ నుండి థ్రెడ్‌కు అమలు కోసం ప్రతినిధిని పంపడానికి ఒక అన్యదేశ మార్గం కూడా ఉంది - బిగిన్‌ఇన్‌వోక్ పద్ధతి.

DelegateInstance.BeginInvoke

Kernel32.dll Win32 API నుండి క్రియేట్ థ్రెడ్ - నేను పైన పేర్కొన్న అనేక పద్ధతులను పిలిచే ఫంక్షన్‌పై క్లుప్తంగా నివసించాలనుకుంటున్నాను. ఒక మార్గం ఉంది, బాహ్య పద్ధతుల యొక్క యంత్రాంగానికి ధన్యవాదాలు, ఈ ఫంక్షన్ కాల్. లెగసీ కోడ్ యొక్క భయంకరమైన ఉదాహరణలో నేను అలాంటి కాల్‌ని ఒక్కసారి మాత్రమే చూశాను మరియు సరిగ్గా దీన్ని చేసిన రచయిత యొక్క ప్రేరణ ఇప్పటికీ నాకు మిస్టరీగా మిగిలిపోయింది.

Kernel32.dll CreateThread

థ్రెడ్‌లను వీక్షించడం మరియు డీబగ్గింగ్ చేయడం

మీరు సృష్టించిన థ్రెడ్‌లు, అన్ని థర్డ్-పార్టీ భాగాలు మరియు .NET పూల్‌ని విజువల్ స్టూడియో థ్రెడ్‌ల విండోలో వీక్షించవచ్చు. అప్లికేషన్ డీబగ్‌లో ఉన్నప్పుడు మరియు బ్రేక్ మోడ్‌లో ఉన్నప్పుడు మాత్రమే ఈ విండో థ్రెడ్ సమాచారాన్ని ప్రదర్శిస్తుంది. ఇక్కడ మీరు ప్రతి థ్రెడ్ యొక్క స్టాక్ పేర్లు మరియు ప్రాధాన్యతలను సౌకర్యవంతంగా వీక్షించవచ్చు మరియు డీబగ్గింగ్‌ను నిర్దిష్ట థ్రెడ్‌కు మార్చవచ్చు. థ్రెడ్ క్లాస్ యొక్క ప్రాధాన్యతా ప్రాపర్టీని ఉపయోగించి, మీరు థ్రెడ్ యొక్క ప్రాధాన్యతను సెట్ చేయవచ్చు, థ్రెడ్‌ల మధ్య ప్రాసెసర్ సమయాన్ని విభజించేటప్పుడు OC మరియు CLR సిఫార్సుగా గ్రహిస్తాయి.

.NET: మల్టీథ్రెడింగ్ మరియు అసమకాలికతో పని చేసే సాధనాలు. 1 వ భాగము

టాస్క్ సమాంతర లైబ్రరీ

టాస్క్ పారలల్ లైబ్రరీ (TPL) .NET 4.0లో ప్రవేశపెట్టబడింది. ఇప్పుడు ఇది అసమకాలికతో పనిచేయడానికి ప్రామాణికం మరియు ప్రధాన సాధనం. పాత విధానాన్ని ఉపయోగించే ఏదైనా కోడ్ లెగసీగా పరిగణించబడుతుంది. TPL యొక్క ప్రాథమిక యూనిట్ System.Threading.Tasks నేమ్‌స్పేస్ నుండి టాస్క్ క్లాస్. టాస్క్ అనేది థ్రెడ్‌పై ఉన్న సంగ్రహణ. C# భాష యొక్క కొత్త వెర్షన్‌తో, మేము టాస్క్‌లతో పని చేయడానికి ఒక సొగసైన మార్గాన్ని పొందాము - async/వెయిట్ ఆపరేటర్‌లు. ఈ భావనలు అసమకాలిక కోడ్‌ను సరళంగా మరియు సమకాలీకరించినట్లుగా వ్రాయడం సాధ్యం చేసింది, ఇది థ్రెడ్‌ల అంతర్గత పనితీరుపై తక్కువ అవగాహన ఉన్న వ్యక్తులకు కూడా వాటిని ఉపయోగించే అప్లికేషన్‌లను వ్రాయడం, సుదీర్ఘ కార్యకలాపాలు చేస్తున్నప్పుడు స్తంభింపజేయని అప్లికేషన్‌లను వ్రాయడం సాధ్యం చేసింది. అసమకాలిక/నిరీక్షణను ఉపయోగించడం అనేది ఒకటి లేదా అనేక కథనాలకు సంబంధించిన అంశం, కానీ నేను దాని సారాంశాన్ని కొన్ని వాక్యాలలో పొందడానికి ప్రయత్నిస్తాను:

  • async అనేది టాస్క్ లేదా శూన్యతను తిరిగి ఇచ్చే పద్ధతి యొక్క మాడిఫైయర్
  • మరియు awaiట్ అనేది నాన్-బ్లాకింగ్ టాస్క్ వెయిటింగ్ ఆపరేటర్.

మరోసారి: వేచి ఉన్న ఆపరేటర్, సాధారణ సందర్భంలో (మినహాయింపులు ఉన్నాయి), ప్రస్తుత అమలు థ్రెడ్‌ను మరింత విడుదల చేస్తుంది మరియు టాస్క్ దాని అమలును పూర్తి చేసినప్పుడు మరియు థ్రెడ్ (వాస్తవానికి, సందర్భాన్ని చెప్పడం మరింత సరైనది. , కానీ తరువాత మరింత) పద్ధతిని అమలు చేయడం కొనసాగుతుంది. .NET లోపల, వ్రాతపూర్వక పద్ధతి మొత్తం తరగతిగా మారినప్పుడు, ఈ విధానం రాబడి రాబడి వలె అమలు చేయబడుతుంది, ఇది రాష్ట్ర యంత్రం మరియు ఈ రాష్ట్రాలపై ఆధారపడి ప్రత్యేక ముక్కలుగా అమలు చేయబడుతుంది. ఆసక్తి ఉన్న ఎవరైనా అసిన్స్/వెయిట్‌ని ఉపయోగించి ఏదైనా సాధారణ కోడ్‌ని వ్రాయవచ్చు, కంపైలర్ జనరేటెడ్ కోడ్ ఎనేబుల్ చేయబడిన 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
}

అనేక ఎంపికలతో టాస్క్ సృష్టించబడుతుంది:

  • లాంగ్‌రన్నింగ్ అనేది పని త్వరగా పూర్తి చేయబడదని సూచన, అంటే పూల్ నుండి థ్రెడ్ తీసుకోకుండా పరిగణించడం విలువైనదే కావచ్చు, కానీ ఇతరులకు హాని కలిగించకుండా ఈ టాస్క్ కోసం ఒక ప్రత్యేకమైనదాన్ని సృష్టించడం.
  • AttachedToParent - విధులను సోపానక్రమంలో అమర్చవచ్చు. ఈ ఎంపికను ఉపయోగించినట్లయితే, టాస్క్ అది పూర్తి చేసిన స్థితిలో ఉండవచ్చు మరియు దాని పిల్లల అమలు కోసం వేచి ఉండవచ్చు.
  • ప్రిఫర్‌ఫెయిర్‌నెస్ - అంటే ముందుగా ఎగ్జిక్యూషన్ కోసం పంపిన టాస్క్‌లను తర్వాత పంపే ముందు ఎగ్జిక్యూట్ చేయడం మంచిది. కానీ ఇది కేవలం సిఫార్సు మాత్రమే మరియు ఫలితాలు హామీ ఇవ్వబడవు.

పద్ధతికి పంపబడిన రెండవ పరామితి CancellationToken. ఆపరేషన్ ప్రారంభించిన తర్వాత దాని రద్దును సరిగ్గా నిర్వహించడానికి, అమలు చేయబడిన కోడ్ తప్పనిసరిగా CancellationToken స్థితికి సంబంధించిన తనిఖీలతో నింపాలి. చెక్‌లు లేనట్లయితే, CancellationTokenSource ఆబ్జెక్ట్‌లో కాల్ చేయబడిన రద్దు పద్ధతి టాస్క్ ప్రారంభమయ్యే ముందు మాత్రమే దాని అమలును ఆపివేయగలదు.

చివరి పరామితి TaskScheduler రకం యొక్క షెడ్యూలర్ ఆబ్జెక్ట్. ఈ తరగతి మరియు దాని వారసులు డిఫాల్ట్‌గా థ్రెడ్‌ల అంతటా టాస్క్‌లను పంపిణీ చేయడానికి వ్యూహాలను నియంత్రించడానికి రూపొందించబడ్డాయి, టాస్క్ పూల్ నుండి యాదృచ్ఛిక థ్రెడ్‌లో అమలు చేయబడుతుంది.

నిరీక్షణ ఆపరేటర్ సృష్టించిన టాస్క్‌కి వర్తింపజేయబడుతుంది, అంటే దాని తర్వాత వ్రాసిన కోడ్, ఒకటి ఉంటే, వేచి ఉండే ముందు కోడ్ వలె అదే సందర్భంలో (తరచుగా దీని అర్థం అదే థ్రెడ్‌లో) అమలు చేయబడుతుంది.

పద్ధతి అసమకాలిక శూన్యతగా గుర్తించబడింది, అంటే ఇది వేచి ఉన్న ఆపరేటర్‌ని ఉపయోగించవచ్చు, కానీ కాలింగ్ కోడ్ అమలు కోసం వేచి ఉండదు. అటువంటి లక్షణం అవసరమైతే, పద్ధతి తప్పనిసరిగా టాస్క్‌ని తిరిగి ఇవ్వాలి. అసమకాలీకరణ శూన్యమని గుర్తించబడిన పద్ధతులు చాలా సాధారణం: నియమం వలె, ఇవి ఈవెంట్ హ్యాండ్లర్లు లేదా అగ్నిపై పని చేసే మరియు సూత్రాన్ని మరచిపోయే ఇతర పద్ధతులు. మీరు అమలు ముగిసే వరకు వేచి ఉండటానికి మాత్రమే కాకుండా, ఫలితాన్ని తిరిగి ఇవ్వాల్సిన అవసరం ఉంటే, మీరు టాస్క్‌ని ఉపయోగించాలి.

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
}

మొదటి ఉదాహరణలో, కాలింగ్ థ్రెడ్‌ను నిరోధించకుండానే మేము టాస్క్ పూర్తయ్యే వరకు వేచి ఉంటాము, అప్పటి వరకు కాలింగ్ థ్రెడ్ దాని స్వంత పరికరాలకు వదిలివేయబడుతుంది;

రెండవ ఎంపికలో, పద్ధతి యొక్క ఫలితం లెక్కించబడే వరకు మేము కాలింగ్ థ్రెడ్‌ను బ్లాక్ చేస్తాము. మేము ప్రోగ్రామ్ యొక్క అటువంటి విలువైన వనరు అయిన థ్రెడ్‌ను సాధారణ పనిలేకుండా ఆక్రమించినందున ఇది చెడ్డది, కానీ మనం పిలిచే పద్ధతి యొక్క కోడ్ వేచి ఉంటే మరియు సమకాలీకరణ సందర్భం తర్వాత కాలింగ్ థ్రెడ్‌కు తిరిగి వెళ్లడం అవసరం. వేచి ఉండండి, అప్పుడు మేము ప్రతిష్టంభన పొందుతాము : అసమకాలిక పద్ధతి యొక్క ఫలితాన్ని లెక్కించేటప్పుడు కాలింగ్ థ్రెడ్ వేచి ఉంటుంది, అసమకాలిక పద్ధతి కాలింగ్ థ్రెడ్‌లో దాని అమలును కొనసాగించడానికి ఫలించలేదు.

ఈ విధానం యొక్క మరొక ప్రతికూలత సంక్లిష్టమైన లోపం నిర్వహణ. వాస్తవం ఏమిటంటే, అసమకాలిక కోడ్‌లో అసమకాలిక కోడ్‌లో లోపాలు అసమకాలిక/నిరీక్షణను ఉపయోగించడం చాలా సులభం - అవి కోడ్ సింక్రోనస్‌గా ఉన్నట్లే ప్రవర్తిస్తాయి. మేము టాస్క్‌కి సింక్రోనస్ వెయిట్ ఎక్స్‌సార్సిజమ్‌ని వర్తింపజేస్తే, అసలు మినహాయింపు మొత్తం మినహాయింపుగా మారుతుంది, అనగా. మినహాయింపును నిర్వహించడానికి, మీరు C# ప్రపంచంలో బాగా తెలిసిన క్యాచ్ బ్లాక్‌ల గొలుసుకు బదులుగా InnerException రకాన్ని పరిశీలించి, ఒక క్యాచ్ బ్లాక్‌లో మీరే ఒక if చైన్‌ను వ్రాయాలి లేదా నిర్మాణ సమయంలో క్యాచ్‌ను ఉపయోగించాలి.

మూడవ మరియు చివరి ఉదాహరణలు కూడా అదే కారణంతో చెడుగా గుర్తించబడ్డాయి మరియు ఒకే రకమైన సమస్యలను కలిగి ఉంటాయి.

టాస్క్‌ల సమూహం కోసం వేచి ఉండటానికి ఎప్పుడు మరియు ఎప్పుడు అన్ని పద్ధతులు చాలా సౌకర్యవంతంగా ఉంటాయి;

థ్రెడ్‌లను ఆపడం

వివిధ కారణాల వల్ల, అది ప్రారంభమైన తర్వాత ప్రవాహాన్ని ఆపడం అవసరం కావచ్చు. దీన్ని చేయడానికి అనేక మార్గాలు ఉన్నాయి. థ్రెడ్ క్లాస్‌లో సముచితంగా పేరున్న రెండు పద్ధతులు ఉన్నాయి: రద్దుచెయ్యి и ఆటంకాన్ని. మొదటిది ఉపయోగం కోసం సిఫారసు చేయబడలేదు, ఎందుకంటే ఏదైనా యాదృచ్ఛిక క్షణంలో కాల్ చేసిన తర్వాత, ఏదైనా సూచనల ప్రాసెసింగ్ సమయంలో, మినహాయింపు విసిరివేయబడుతుంది ThreadAbortedException. ఏదైనా పూర్ణాంకం వేరియబుల్‌ని పెంచేటప్పుడు అటువంటి మినహాయింపు ఇవ్వబడుతుందని మీరు ఆశించరు, సరియైనదా? మరియు ఈ పద్ధతిని ఉపయోగిస్తున్నప్పుడు, ఇది చాలా నిజమైన పరిస్థితి. కోడ్‌లోని నిర్దిష్ట విభాగంలో అటువంటి మినహాయింపును రూపొందించకుండా CLRని నిరోధించాల్సిన అవసరం ఉంటే, మీరు దానిని కాల్‌లలో వ్రాప్ చేయవచ్చు Thread.BeginCriticalRegion, Thread.EndCriticalRegion. చివరగా బ్లాక్‌లో వ్రాసిన ఏదైనా కోడ్ అటువంటి కాల్‌లలో చుట్టబడి ఉంటుంది. ఈ కారణంగా, ఫ్రేమ్‌వర్క్ కోడ్ యొక్క లోతుల్లో మీరు ఖాళీ ప్రయత్నంతో బ్లాక్‌లను కనుగొనవచ్చు, కానీ చివరకు ఖాళీగా ఉండకూడదు. మైక్రోసాఫ్ట్ ఈ పద్ధతిని నిరుత్సాహపరుస్తుంది కాబట్టి వారు దీనిని .net కోర్లో చేర్చలేదు.

అంతరాయ పద్ధతి మరింత ఊహాజనితంగా పనిచేస్తుంది. ఇది మినహాయింపుతో థ్రెడ్‌కు అంతరాయం కలిగించవచ్చు ThreadInterruptedException థ్రెడ్ వేచి ఉండే స్థితిలో ఉన్నప్పుడు మాత్రమే. WaitHandle, లాక్ లేదా Thread.Sleepకి కాల్ చేసిన తర్వాత వేలాడుతున్నప్పుడు ఇది ఈ స్థితిలోకి ప్రవేశిస్తుంది.

పైన వివరించిన రెండు ఎంపికలు వాటి అనూహ్యత కారణంగా చెడ్డవి. పరిష్కారం ఒక నిర్మాణాన్ని ఉపయోగించడం రద్దు టోకెన్ మరియు తరగతి రద్దు టోకెన్సోర్స్. విషయం ఇది: CancellationTokenSource తరగతి యొక్క ఉదాహరణ సృష్టించబడింది మరియు దానిని కలిగి ఉన్న వ్యక్తి మాత్రమే పద్ధతికి కాల్ చేయడం ద్వారా ఆపరేషన్‌ను ఆపగలరు రద్దు. కేవలం CancellationToken మాత్రమే ఆపరేషన్‌కు పంపబడుతుంది. CancellationToken యజమానులు స్వయంగా ఆపరేషన్‌ను రద్దు చేయలేరు, కానీ ఆపరేషన్ రద్దు చేయబడిందో లేదో మాత్రమే తనిఖీ చేయగలరు. దీని కోసం బూలియన్ ఆస్తి ఉంది రద్దు అభ్యర్థించబడింది మరియు పద్ధతి త్రోIfCancel అభ్యర్థించబడింది. తరువాతి మినహాయింపును విసురుతుంది TaskCancelledException క్యాన్సిలేషన్ టోకెన్ ఉదాహరణపై రద్దు పద్ధతిని పిలిస్తే, చిలుక వేయబడుతుంది. మరియు ఇది నేను ఉపయోగించమని సిఫార్సు చేసిన పద్ధతి. మినహాయింపు ఆపరేషన్ ఏ సమయంలో నిలిపివేయబడుతుందనే దానిపై పూర్తి నియంత్రణను పొందడం ద్వారా ఇది మునుపటి ఎంపికల కంటే మెరుగుదల.

థ్రెడ్‌ను ఆపడానికి అత్యంత క్రూరమైన ఎంపిక Win32 API TerminateThread ఫంక్షన్‌కు కాల్ చేయడం. ఈ ఫంక్షన్‌కు కాల్ చేసిన తర్వాత CLR యొక్క ప్రవర్తన అనూహ్యంగా ఉండవచ్చు. MSDNలో ఈ ఫంక్షన్ గురించి ఈ క్రింది విధంగా వ్రాయబడింది: “టెర్మినేట్ థ్రెడ్ అనేది అత్యంత తీవ్రమైన సందర్భాల్లో మాత్రమే ఉపయోగించబడే ప్రమాదకరమైన ఫంక్షన్. "

FromAsync పద్ధతిని ఉపయోగించి లెగసీ APIని టాస్క్ బేస్డ్‌గా మారుస్తోంది

మీరు టాస్క్‌లు ప్రవేశపెట్టిన తర్వాత ప్రారంభించబడిన ప్రాజెక్ట్‌లో పని చేసే అదృష్టం కలిగి ఉంటే మరియు చాలా మంది డెవలపర్‌లకు నిశ్శబ్ద భయాందోళన కలిగించడం మానేసినట్లయితే, మీరు చాలా పాత APIలతో, మూడవ పక్షం మరియు మీ బృందంతో వ్యవహరించాల్సిన అవసరం లేదు. గతంలో చిత్రహింసలు పెట్టింది. అదృష్టవశాత్తూ, .NET ఫ్రేమ్‌వర్క్ బృందం మమ్మల్ని జాగ్రత్తగా చూసుకుంది, అయితే బహుశా మనల్ని మనం జాగ్రత్తగా చూసుకోవడం లక్ష్యం కావచ్చు. ఏది ఏమైనప్పటికీ, పాత అసమకాలిక ప్రోగ్రామింగ్ విధానాలలో వ్రాసిన కోడ్‌ను నొప్పిలేకుండా కొత్తదానికి మార్చడానికి .NET అనేక సాధనాలను కలిగి ఉంది. వాటిలో ఒకటి TaskFactory యొక్క FromAsync పద్ధతి. దిగువ కోడ్ ఉదాహరణలో, నేను ఈ పద్ధతిని ఉపయోగించి టాస్క్‌లో WebRequest తరగతి యొక్క పాత అసమకాలీకరణ పద్ధతులను చుట్టేస్తాను.

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

ఇది కేవలం ఒక ఉదాహరణ మాత్రమే మరియు మీరు అంతర్నిర్మిత రకాలతో దీన్ని చేయవలసిన అవసరం లేదు, కానీ ఏదైనా పాత ప్రాజెక్ట్ కేవలం IAsyncResult మరియు EndDoSomething పద్ధతులను స్వీకరించే BeginDoSomething పద్ధతులతో నిండి ఉంటుంది.

TaskCompletionSource తరగతిని ఉపయోగించి లెగసీ APIని టాస్క్ బేస్డ్‌గా మార్చండి

పరిగణించవలసిన మరో ముఖ్యమైన సాధనం తరగతి TaskCompletionSource. విధులు, ప్రయోజనం మరియు ఆపరేషన్ సూత్రం పరంగా, ఇది నేను పైన వ్రాసిన ThreadPool తరగతి యొక్క RegisterWaitForSingleObject పద్ధతిని కొంతవరకు గుర్తుకు తెస్తుంది. ఈ తరగతిని ఉపయోగించి, మీరు టాస్క్‌లలో పాత అసమకాలిక APIలను సులభంగా మరియు సౌకర్యవంతంగా చుట్టవచ్చు.

ఈ ప్రయోజనాల కోసం ఉద్దేశించిన టాస్క్‌ఫ్యాక్టరీ క్లాస్ యొక్క FromAsync పద్ధతి గురించి నేను ఇప్పటికే మాట్లాడానని మీరు చెబుతారు. మైక్రోసాఫ్ట్ గత 15 సంవత్సరాలుగా అందించిన .net లో అసమకాలిక నమూనాల అభివృద్ధి యొక్క పూర్తి చరిత్రను ఇక్కడ మనం గుర్తుంచుకోవాలి: టాస్క్-బేస్డ్ ఎసిన్క్రోనస్ ప్యాటర్న్ (TAP) కంటే ముందు, అసమకాలిక ప్రోగ్రామింగ్ ప్యాటర్న్ (APP) ఉండేది. పద్ధతుల గురించి ప్రారంభంఏదో తిరిగి వస్తోంది IAsyncResult మరియు పద్ధతులు చివరదీన్ని అంగీకరించే మరియు ఈ సంవత్సరాల వారసత్వం కోసం FromAsync పద్ధతి ఖచ్చితంగా ఉంది, కానీ కాలక్రమేణా, ఇది ఈవెంట్ ఆధారిత అసమకాలిక నమూనా ద్వారా భర్తీ చేయబడింది (EAP), ఇది అసమకాలిక ఆపరేషన్ పూర్తయినప్పుడు ఒక ఈవెంట్ లేవనెత్తుతుందని ఊహించబడింది.

TaskCompletionSource ఈవెంట్ మోడల్ చుట్టూ నిర్మించబడిన టాస్క్‌లు మరియు లెగసీ APIలను చుట్టడానికి సరైనది. దాని పని యొక్క సారాంశం క్రింది విధంగా ఉంది: ఈ తరగతి యొక్క వస్తువు టాస్క్ రకం యొక్క పబ్లిక్ ప్రాపర్టీని కలిగి ఉంది, దీని స్థితిని టాస్క్‌కాంప్లీషన్‌సోర్స్ క్లాస్ యొక్క సెట్ రిజల్ట్, సెట్‌ఎక్సెప్షన్ మొదలైన పద్ధతుల ద్వారా నియంత్రించవచ్చు. ఈ టాస్క్‌కి వేచి ఉన్న ఆపరేటర్‌ని వర్తింపజేసిన ప్రదేశాలలో, ఇది 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;
}

TaskCompletionSource చిట్కాలు & ఉపాయాలు

TaskCompletionSourceని ఉపయోగించి పాత APIలను చుట్టడం అంతా ఇంతా కాదు. ఈ తరగతిని ఉపయోగించడం వలన థ్రెడ్‌లను ఆక్రమించని టాస్క్‌లపై వివిధ APIలను రూపొందించే ఆసక్తికరమైన అవకాశం అందుబాటులోకి వస్తుంది. మరియు స్ట్రీమ్, మనకు గుర్తున్నట్లుగా, ఖరీదైన వనరు మరియు వాటి సంఖ్య పరిమితం చేయబడింది (ప్రధానంగా RAM మొత్తం ద్వారా). ఈ పరిమితిని అభివృద్ధి చేయడం ద్వారా సులభంగా సాధించవచ్చు, ఉదాహరణకు, సంక్లిష్ట వ్యాపార తర్కంతో లోడ్ చేయబడిన వెబ్ అప్లికేషన్. లాంగ్-పోలింగ్ వంటి ట్రిక్‌ను అమలు చేసేటప్పుడు నేను మాట్లాడుతున్న అవకాశాలను పరిశీలిద్దాం.

సంక్షిప్తంగా, ట్రిక్ యొక్క సారాంశం ఇది: మీరు API నుండి దాని వైపు సంభవించే కొన్ని ఈవెంట్‌ల గురించి సమాచారాన్ని స్వీకరించాలి, అయితే API, కొన్ని కారణాల వల్ల, ఈవెంట్‌ను నివేదించదు, కానీ స్థితిని మాత్రమే తిరిగి ఇవ్వగలదు. WebSocket సమయానికి ముందు లేదా కొన్ని కారణాల వల్ల ఈ సాంకేతికతను ఉపయోగించడం అసాధ్యం అయినప్పుడు HTTP పైన రూపొందించబడిన అన్ని APIలు వీటికి ఉదాహరణ. క్లయింట్ HTTP సర్వర్‌ని అడగవచ్చు. HTTP సర్వర్ క్లయింట్‌తో కమ్యూనికేషన్‌ను ప్రారంభించలేదు. టైమర్‌ని ఉపయోగించి సర్వర్‌ను పోల్ చేయడం ఒక సాధారణ పరిష్కారం, అయితే ఇది సర్వర్‌పై అదనపు లోడ్‌ను సృష్టిస్తుంది మరియు సగటు టైమర్‌ఇంటర్వెల్ / 2పై అదనపు ఆలస్యాన్ని సృష్టిస్తుంది. దీన్ని అధిగమించడానికి, లాంగ్ పోలింగ్ అనే ఉపాయం కనుగొనబడింది, ఇందులో ప్రతిస్పందన ఆలస్యం అవుతుంది గడువు ముగిసే వరకు లేదా ఈవెంట్ సంభవించే వరకు సర్వర్. ఈవెంట్ సంభవించినట్లయితే, అది ప్రాసెస్ చేయబడుతుంది, కాకపోతే, అభ్యర్థన మళ్లీ పంపబడుతుంది.

while(!eventOccures && !timeoutExceeded)  {

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

ఈవెంట్ కోసం వేచి ఉన్న క్లయింట్ల సంఖ్య పెరిగిన వెంటనే అలాంటి పరిష్కారం భయంకరంగా ఉంటుంది, ఎందుకంటే... అటువంటి ప్రతి క్లయింట్ ఈవెంట్ కోసం వేచి ఉన్న మొత్తం థ్రెడ్‌ను ఆక్రమిస్తుంది. అవును, మరియు ఈవెంట్ ట్రిగ్గర్ అయినప్పుడు మేము అదనంగా 1మిస్ ఆలస్యాన్ని పొందుతాము, చాలా తరచుగా ఇది ముఖ్యమైనది కాదు, అయితే సాఫ్ట్‌వేర్‌ను దాని కంటే ఎందుకు అధ్వాన్నంగా చేస్తుంది? మేము 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ని సృష్టించి, ఉంచుతాము, ఆపై ముందుగా ఏమి జరుగుతుందో వేచి ఉండండి: పేర్కొన్న సమయ విరామం ముగుస్తుంది లేదా సందేశం స్వీకరించబడుతుంది.

విలువ టాస్క్: ఎందుకు మరియు ఎలా

ఈల్డ్ రిటర్న్ ఆపరేటర్ వంటి సింక్/వెయిట్ ఆపరేటర్‌లు ఈ పద్ధతి నుండి స్టేట్ మెషీన్‌ను రూపొందిస్తారు మరియు ఇది కొత్త వస్తువు యొక్క సృష్టి, ఇది దాదాపు ఎల్లప్పుడూ ముఖ్యమైనది కాదు, కానీ అరుదైన సందర్భాల్లో ఇది సమస్యను సృష్టించవచ్చు. ఈ కేసు నిజంగా తరచుగా పిలువబడే ఒక పద్ధతి కావచ్చు, మేము సెకనుకు పదుల మరియు వందల వేల కాల్స్ గురించి మాట్లాడుతున్నాము. అటువంటి పద్ధతిని చాలా సందర్భాలలో అది అన్ని నిరీక్షణ పద్ధతులను దాటవేసి ఫలితాన్ని అందించే విధంగా వ్రాసినట్లయితే, .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 వ్యూహాలను నిర్వహించగల సామర్థ్యాన్ని కలిగి ఉందని నేను ఇప్పటికే పైన పేర్కొన్నాను. ఇటువంటి వ్యూహాలు టాస్క్‌షెడ్యూలర్ తరగతి వారసులలో నిర్వచించబడ్డాయి. మీకు అవసరమైన ఏదైనా వ్యూహాన్ని లైబ్రరీలో కనుగొనవచ్చు. సమాంతర పొడిగింపులు ఎక్స్‌ట్రాలు, మైక్రోసాఫ్ట్ అభివృద్ధి చేసింది, కానీ .NETలో భాగం కాదు, న్యూగెట్ ప్యాకేజీగా సరఫరా చేయబడింది. వాటిలో కొన్నింటిని క్లుప్తంగా చూద్దాం:

  • CurrentThreadTaskScheduler — ప్రస్తుత థ్రెడ్‌లో టాస్క్‌లను అమలు చేస్తుంది
  • లిమిటెడ్ ConcurrencyLevelTaskScheduler - కన్స్ట్రక్టర్‌లో ఆమోదించబడిన N పారామీటర్ ద్వారా ఏకకాలంలో అమలు చేయబడిన పనుల సంఖ్యను పరిమితం చేస్తుంది
  • ఆర్డర్ చేసిన టాస్క్ షెడ్యూలర్ — LimitedConcurrencyLevelTaskScheduler(1)గా నిర్వచించబడింది, కాబట్టి పనులు వరుసగా అమలు చేయబడతాయి.
  • WorkStealingTaskScheduler - అమలు చేస్తుంది పని-దొంగతనం పని పంపిణీ విధానం. ముఖ్యంగా ఇది ఒక ప్రత్యేక ThreadPool. .NET థ్రెడ్‌పూల్‌లో స్టాటిక్ క్లాస్, అన్ని అప్లికేషన్‌లకు ఒకటి అనే సమస్యను పరిష్కరిస్తుంది, అంటే ప్రోగ్రామ్‌లోని ఒక భాగంలో దాని ఓవర్‌లోడింగ్ లేదా తప్పుగా ఉపయోగించడం వల్ల మరొక భాగంలో దుష్ప్రభావాలకు దారితీయవచ్చు. అదనంగా, అటువంటి లోపాల యొక్క కారణాన్ని అర్థం చేసుకోవడం చాలా కష్టం. ఆ. థ్రెడ్‌పూల్ ఉపయోగం దూకుడుగా మరియు అనూహ్యంగా ఉండే ప్రోగ్రామ్‌లోని భాగాలలో ప్రత్యేక వర్క్‌స్టీలింగ్ టాస్క్‌షెడ్యూలర్‌లను ఉపయోగించాల్సిన అవసరం ఉండవచ్చు.
  • QueuedTaskScheduler - ప్రాధాన్యతా క్యూ నియమాల ప్రకారం పనులను నిర్వహించడానికి మిమ్మల్ని అనుమతిస్తుంది
  • ThreadPerTaskScheduler — దానిపై అమలు చేయబడిన ప్రతి పనికి ప్రత్యేక థ్రెడ్‌ను సృష్టిస్తుంది. పూర్తి చేయడానికి అనూహ్యంగా ఎక్కువ సమయం పట్టే పనులకు ఉపయోగపడుతుంది.

మంచి వివరంగా ఉంది వ్యాసం మైక్రోసాఫ్ట్ బ్లాగ్‌లో టాస్క్‌షెడ్యూలర్‌ల గురించి.

టాస్క్‌లకు సంబంధించిన ప్రతిదానికీ అనుకూలమైన డీబగ్గింగ్ కోసం, విజువల్ స్టూడియోలో టాస్క్‌ల విండో ఉంది. ఈ విండోలో మీరు పని యొక్క ప్రస్తుత స్థితిని చూడవచ్చు మరియు ప్రస్తుతం అమలులో ఉన్న కోడ్ లైన్‌కు వెళ్లవచ్చు.

.NET: మల్టీథ్రెడింగ్ మరియు అసమకాలికతో పని చేసే సాధనాలు. 1 వ భాగము

PLinq మరియు సమాంతర తరగతి

టాస్క్‌లు మరియు వాటి గురించి చెప్పిన ప్రతిదానితో పాటు, .NETలో మరో రెండు ఆసక్తికరమైన సాధనాలు ఉన్నాయి: PLinq (Linq2Parallel) మరియు సమాంతర తరగతి. మొదటిది బహుళ థ్రెడ్‌లలో అన్ని లింక్ కార్యకలాపాలను సమాంతరంగా అమలు చేయడానికి హామీ ఇస్తుంది. WithDegreeOfParallelism పొడిగింపు పద్ధతిని ఉపయోగించి థ్రెడ్‌ల సంఖ్యను కాన్ఫిగర్ చేయవచ్చు. దురదృష్టవశాత్తూ, చాలా తరచుగా PLinq దాని డిఫాల్ట్ మోడ్‌లో గణనీయమైన వేగాన్ని అందించడానికి మీ డేటా మూలం యొక్క అంతర్గత గురించి తగినంత సమాచారాన్ని కలిగి ఉండదు, మరోవైపు, ప్రయత్నించడానికి అయ్యే ఖర్చు చాలా తక్కువగా ఉంటుంది: మీరు ముందుగా AsParallel పద్ధతిని కాల్ చేయాలి. లింక్ పద్ధతుల గొలుసు మరియు పనితీరు పరీక్షలను అమలు చేయండి. అంతేకాకుండా, విభజనల యంత్రాంగాన్ని ఉపయోగించి మీ డేటా మూలం యొక్క స్వభావం గురించి PLinqకి అదనపు సమాచారాన్ని పంపడం సాధ్యమవుతుంది. మీరు మరింత చదవగలరు ఇక్కడ и ఇక్కడ.

సమాంతర స్టాటిక్ క్లాస్ ఫోర్చ్ సేకరణ ద్వారా సమాంతరంగా పునరావృతం చేయడానికి, ఫర్ లూప్‌ని అమలు చేయడానికి మరియు సమాంతర ఇన్‌వోక్‌లో బహుళ ప్రతినిధులను అమలు చేయడానికి పద్ధతులను అందిస్తుంది. లెక్కలు పూర్తయ్యే వరకు ప్రస్తుత థ్రెడ్ యొక్క అమలు నిలిపివేయబడుతుంది. చివరి ఆర్గ్యుమెంట్‌గా ParallelOptionsని పాస్ చేయడం ద్వారా థ్రెడ్‌ల సంఖ్యను కాన్ఫిగర్ చేయవచ్చు. మీరు ఎంపికలను ఉపయోగించి TaskScheduler మరియు CancellationTokenని కూడా పేర్కొనవచ్చు.

కనుగొన్న

నా నివేదికలోని మెటీరియల్స్ మరియు దాని తర్వాత నా పనిలో నేను సేకరించిన సమాచారం ఆధారంగా ఈ వ్యాసం రాయడం ప్రారంభించినప్పుడు, ఇది చాలా ఉంటుందని నేను ఊహించలేదు. ఇప్పుడు, నేను ఈ కథనాన్ని టైప్ చేస్తున్న టెక్స్ట్ ఎడిటర్ 15వ పేజీ పోయిందని నిందతో చెప్పినప్పుడు, నేను మధ్యంతర ఫలితాలను సంగ్రహిస్తాను. ఇతర ఉపాయాలు, APIలు, విజువల్ టూల్స్ మరియు ఆపదలు తదుపరి కథనంలో కవర్ చేయబడతాయి.

ముగింపులు:

  • ఆధునిక PCల వనరులను ఉపయోగించడానికి మీరు థ్రెడ్‌లు, అసమకాలికత మరియు సమాంతరతతో పని చేసే సాధనాలను తెలుసుకోవాలి.
  • .NET ఈ ప్రయోజనాల కోసం అనేక విభిన్న సాధనాలను కలిగి ఉంది
  • అవన్నీ ఒకేసారి కనిపించలేదు, కాబట్టి మీరు తరచుగా లెగసీ వాటిని కనుగొనవచ్చు, అయినప్పటికీ, పాత API లను ఎక్కువ శ్రమ లేకుండా మార్చడానికి మార్గాలు ఉన్నాయి.
  • .NETలో థ్రెడ్‌లతో పని చేయడం థ్రెడ్ మరియు థ్రెడ్‌పూల్ తరగతుల ద్వారా సూచించబడుతుంది
  • Thread.Abort, Thread.Interrupt, మరియు Win32 API TerminateThread పద్ధతులు ప్రమాదకరమైనవి మరియు ఉపయోగం కోసం సిఫార్సు చేయబడవు. బదులుగా, CancellationToken మెకానిజంను ఉపయోగించడం మంచిది
  • ప్రవాహం విలువైన వనరు మరియు దాని సరఫరా పరిమితం. ఈవెంట్‌ల కోసం థ్రెడ్‌లు బిజీగా వేచి ఉండే పరిస్థితులను నివారించాలి. దీని కోసం TaskCompletionSource తరగతిని ఉపయోగించడం సౌకర్యంగా ఉంటుంది
  • సమాంతరత మరియు అసమకాలికతతో పనిచేయడానికి అత్యంత శక్తివంతమైన మరియు అధునాతన .NET సాధనాలు టాస్క్‌లు.
  • c# async/await ఆపరేటర్లు నాన్-బ్లాకింగ్ వెయిట్ భావనను అమలు చేస్తారు
  • మీరు TaskScheduler-ఉత్పన్న తరగతులను ఉపయోగించి థ్రెడ్‌ల అంతటా టాస్క్‌ల పంపిణీని నియంత్రించవచ్చు
  • హాట్-పాత్‌లు మరియు మెమరీ-ట్రాఫిక్‌ను ఆప్టిమైజ్ చేయడంలో ValueTask నిర్మాణం ఉపయోగపడుతుంది
  • విజువల్ స్టూడియో యొక్క టాస్క్‌లు మరియు థ్రెడ్‌ల విండోలు బహుళ-థ్రెడ్ లేదా అసమకాలిక కోడ్‌ను డీబగ్గింగ్ చేయడానికి చాలా ఉపయోగకరమైన సమాచారాన్ని అందిస్తాయి
  • PLinq అనేది ఒక చక్కని సాధనం, కానీ అది మీ డేటా సోర్స్ గురించి తగినంత సమాచారాన్ని కలిగి ఉండకపోవచ్చు, కానీ విభజన విధానం ఉపయోగించి దీన్ని పరిష్కరించవచ్చు
  • కొనసాగించాలి…

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి