ജാവാസ്ക്രിപ്റ്റിൽ യഥാർത്ഥ മൾട്ടിത്രെഡിംഗ് സാധ്യമാക്കുക. ഈ സമഗ്രമായ ഗൈഡ് SharedArrayBuffer, Atomics, വെബ് വർക്കേഴ്സ്, ഉയർന്ന പ്രകടനമുള്ള വെബ് ആപ്ലിക്കേഷനുകൾക്കുള്ള സുരക്ഷാ ആവശ്യകതകൾ എന്നിവയെക്കുറിച്ച് വിശദീകരിക്കുന്നു.
ജാവാസ്ക്രിപ്റ്റ് SharedArrayBuffer: വെബിലെ കൺകറന്റ് പ്രോഗ്രാമിംഗിനെക്കുറിച്ചുള്ള ഒരു സമഗ്ര വിശകലനം
പതിറ്റാണ്ടുകളായി, ജാവാസ്ക്രിപ്റ്റിന്റെ സിംഗിൾ-ത്രെഡഡ് സ്വഭാവം അതിന്റെ ലാളിത്യത്തിന്റെ ഉറവിടവും അതേസമയം പ്രകടനത്തിലെ ഒരു പ്രധാന തടസ്സവുമായിരുന്നു. ഇവന്റ് ലൂപ്പ് മോഡൽ മിക്ക യുഐ-ഡ്രിവൺ ജോലികൾക്കും മനോഹരമായി പ്രവർത്തിക്കുന്നു, പക്ഷേ കമ്പ്യൂട്ടേഷണൽ ഭാരമേറിയ പ്രവർത്തനങ്ങളെ അഭിമുഖീകരിക്കുമ്പോൾ അത് ബുദ്ധിമുട്ടുന്നു. ദീർഘനേരം പ്രവർത്തിക്കുന്ന കണക്കുകൂട്ടലുകൾക്ക് ബ്രൗസറിനെ മരവിപ്പിക്കാൻ കഴിയും, ഇത് ഉപയോക്താവിന് നിരാശാജനകമായ അനുഭവം നൽകുന്നു. വെബ് വർക്കേഴ്സ് സ്ക്രിപ്റ്റുകളെ പശ്ചാത്തലത്തിൽ പ്രവർത്തിക്കാൻ അനുവദിച്ചുകൊണ്ട് ഒരു ഭാഗിക പരിഹാരം നൽകിയെങ്കിലും, അവയ്ക്ക് അവയുടേതായ ഒരു പ്രധാന പരിമിതിയുണ്ടായിരുന്നു: കാര്യക്ഷമമല്ലാത്ത ഡാറ്റാ ആശയവിനിമയം.
`SharedArrayBuffer` (SAB) -ലേക്ക് വരാം, ഇത് വെബിലെ ത്രെഡുകൾക്കിടയിൽ യഥാർത്ഥവും താഴ്ന്ന നിലയിലുള്ളതുമായ മെമ്മറി പങ്കിടൽ അവതരിപ്പിച്ച് കളിയുടെ ഗതിയെ അടിസ്ഥാനപരമായി മാറ്റുന്ന ഒരു ശക്തമായ ഫീച്ചറാണ്. `Atomics` ഒബ്ജക്റ്റുമായി ചേർന്ന്, SAB ഉയർന്ന പ്രകടനമുള്ള, കൺകറന്റ് ആപ്ലിക്കേഷനുകളുടെ ഒരു പുതിയ യുഗം നേരിട്ട് ബ്രൗസറിൽ തുറക്കുന്നു. എന്നിരുന്നാലും, വലിയ ശക്തിയുടെ കൂടെ വലിയ ഉത്തരവാദിത്തവും സങ്കീർണ്ണതയും വരുന്നു.
ഈ ഗൈഡ് നിങ്ങളെ ജാവാസ്ക്രിപ്റ്റിലെ കൺകറന്റ് പ്രോഗ്രാമിംഗിന്റെ ലോകത്തേക്ക് ഒരു ആഴത്തിലുള്ള യാത്രയ്ക്ക് കൊണ്ടുപോകും. നമുക്ക് ഇത് എന്തിന് വേണം, `SharedArrayBuffer`-ഉം `Atomics`-ഉം എങ്ങനെ പ്രവർത്തിക്കുന്നു, നിങ്ങൾ അഭിസംബോധന ചെയ്യേണ്ട നിർണ്ണായക സുരക്ഷാ പരിഗണനകൾ, കൂടാതെ നിങ്ങൾക്ക് ആരംഭിക്കുന്നതിനുള്ള പ്രായോഗിക ഉദാഹരണങ്ങൾ എന്നിവയെല്ലാം നമ്മൾ പര്യവേക്ഷണം ചെയ്യും.
പഴയ ലോകം: ജാവാസ്ക്രിപ്റ്റിന്റെ സിംഗിൾ-ത്രെഡഡ് മോഡലും അതിന്റെ പരിമിതികളും
പരിഹാരത്തെ അഭിനന്ദിക്കുന്നതിന് മുമ്പ്, നമ്മൾ പ്രശ്നം പൂർണ്ണമായി മനസ്സിലാക്കണം. ഒരു ബ്രൗസറിലെ ജാവാസ്ക്രിപ്റ്റ് എക്സിക്യൂഷൻ പരമ്പരാഗതമായി ഒരു സിംഗിൾ ത്രെഡിലാണ് നടക്കുന്നത്, ഇതിനെ "മെയിൻ ത്രെഡ്" അല്ലെങ്കിൽ "യുഐ ത്രെഡ്" എന്ന് വിളിക്കുന്നു.
ഇവന്റ് ലൂപ്പ്
മെയിൻ ത്രെഡ് എല്ലാത്തിനും ഉത്തരവാദിയാണ്: നിങ്ങളുടെ ജാവാസ്ക്രിപ്റ്റ് കോഡ് എക്സിക്യൂട്ട് ചെയ്യുക, പേജ് റെൻഡർ ചെയ്യുക, ഉപയോക്തൃ ഇടപെടലുകളോട് (ക്ലിക്കുകളും സ്ക്രോളുകളും പോലുള്ളവ) പ്രതികരിക്കുക, സിഎസ്എസ് ആനിമേഷനുകൾ പ്രവർത്തിപ്പിക്കുക എന്നിവയെല്ലാം. ഒരു ഇവന്റ് ലൂപ്പ് ഉപയോഗിച്ചാണ് ഇത് ഈ ജോലികൾ കൈകാര്യം ചെയ്യുന്നത്, ഇത് സന്ദേശങ്ങളുടെ (ടാസ്കുകളുടെ) ഒരു ക്യൂ തുടർച്ചയായി പ്രോസസ്സ് ചെയ്യുന്നു. ഒരു ടാസ്ക് പൂർത്തിയാക്കാൻ കൂടുതൽ സമയമെടുത്താൽ, അത് മുഴുവൻ ക്യൂവിനെയും തടയും. മറ്റൊന്നും സംഭവിക്കാൻ കഴിയില്ല - യുഐ മരവിക്കുന്നു, ആനിമേഷനുകൾ തടസ്സപ്പെടുന്നു, പേജ് പ്രതികരണരഹിതമാകുന്നു.
വെബ് വർക്കേഴ്സ്: ശരിയായ ദിശയിലേക്കുള്ള ഒരു ചുവടുവെപ്പ്
ഈ പ്രശ്നം ലഘൂകരിക്കുന്നതിനാണ് വെബ് വർക്കേഴ്സ് അവതരിപ്പിച്ചത്. ഒരു വെബ് വർക്കർ എന്നത് ഒരു പ്രത്യേക പശ്ചാത്തല ത്രെഡിൽ പ്രവർത്തിക്കുന്ന ഒരു സ്ക്രിപ്റ്റാണ്. നിങ്ങൾക്ക് ഭാരമേറിയ കണക്കുകൂട്ടലുകൾ ഒരു വർക്കറിലേക്ക് ഓഫ്ലോഡ് ചെയ്യാം, അതുവഴി യൂസർ ഇന്റർഫേസ് കൈകാര്യം ചെയ്യാൻ മെയിൻ ത്രെഡിനെ സ്വതന്ത്രമായി നിർത്താം.
മെയിൻ ത്രെഡും ഒരു വർക്കറും തമ്മിലുള്ള ആശയവിനിമയം `postMessage()` API വഴിയാണ് നടക്കുന്നത്. നിങ്ങൾ ഡാറ്റ അയയ്ക്കുമ്പോൾ, അത് structured clone algorithm ഉപയോഗിച്ചാണ് കൈകാര്യം ചെയ്യുന്നത്. ഇതിനർത്ഥം ഡാറ്റ സീരിയലൈസ് ചെയ്യുകയും, പകർത്തുകയും, തുടർന്ന് വർക്കറിന്റെ കോൺടെക്സ്റ്റിൽ ഡിസീരിയലൈസ് ചെയ്യുകയും ചെയ്യുന്നു. ഇത് ഫലപ്രദമാണെങ്കിലും, വലിയ ഡാറ്റാസെറ്റുകൾക്ക് ഈ പ്രക്രിയയ്ക്ക് കാര്യമായ പോരായ്മകളുണ്ട്:
- പ്രകടനപരമായ അധികച്ചെലവ്: ത്രെഡുകൾക്കിടയിൽ മെഗാബൈറ്റുകളോ ഗിഗാബൈറ്റുകളോ ഡാറ്റ പകർത്തുന്നത് വേഗത കുറഞ്ഞതും സിപിയു-ഇന്റൻസീവുമാണ്.
- മെമ്മറി ഉപഭോഗം: ഇത് മെമ്മറിയിൽ ഡാറ്റയുടെ ഒരു തനിപ്പകർപ്പ് സൃഷ്ടിക്കുന്നു, ഇത് മെമ്മറി പരിമിതിയുള്ള ഉപകരണങ്ങൾക്ക് ഒരു പ്രധാന പ്രശ്നമാകും.
ബ്രൗസറിലെ ഒരു വീഡിയോ എഡിറ്റർ സങ്കൽപ്പിക്കുക. ഒരു മുഴുവൻ വീഡിയോ ഫ്രെയിം (അതിന് പല മെഗാബൈറ്റുകൾ വലുപ്പമുണ്ടാകാം) സെക്കൻഡിൽ 60 തവണ പ്രോസസ്സ് ചെയ്യുന്നതിനായി ഒരു വർക്കറിലേക്ക് അങ്ങോട്ടും ഇങ്ങോട്ടും അയക്കുന്നത് താങ്ങാനാവാത്തത്ര ചെലവേറിയതായിരിക്കും. ഈ പ്രശ്നം പരിഹരിക്കുന്നതിനാണ് `SharedArrayBuffer` രൂപകൽപ്പന ചെയ്തിരിക്കുന്നത്.
കളിയുടെ ഗതി മാറ്റുന്നത്: `SharedArrayBuffer`-നെ പരിചയപ്പെടുത്തുന്നു
ഒരു `SharedArrayBuffer` എന്നത് `ArrayBuffer`-ന് സമാനമായ, ഒരു നിശ്ചിത നീളമുള്ള റോ ബൈനറി ഡാറ്റാ ബഫറാണ്. നിർണ്ണായകമായ വ്യത്യാസം, ഒരു `SharedArrayBuffer` ഒന്നിലധികം ത്രെഡുകളിൽ (ഉദാഹരണത്തിന്, മെയിൻ ത്രെഡും ഒന്നോ അതിലധികമോ വെബ് വർക്കേഴ്സും) പങ്കിടാൻ കഴിയും എന്നതാണ്. നിങ്ങൾ `postMessage()` ഉപയോഗിച്ച് ഒരു `SharedArrayBuffer` 'അയയ്ക്കുമ്പോൾ', നിങ്ങൾ ഒരു പകർപ്പല്ല അയക്കുന്നത്; നിങ്ങൾ ഒരേ മെമ്മറി ബ്ലോക്കിലേക്കുള്ള ഒരു റഫറൻസാണ് അയക്കുന്നത്.
ഇതിനർത്ഥം ഒരു ത്രെഡ് ബഫറിന്റെ ഡാറ്റയിൽ വരുത്തുന്ന ഏത് മാറ്റങ്ങളും അതിലേക്ക് റഫറൻസുള്ള മറ്റെല്ലാ ത്രെഡുകൾക്കും തൽക്ഷണം ദൃശ്യമാകും. ഇത് ചെലവേറിയ കോപ്പി-ആൻഡ്-സീരിയലൈസ് ഘട്ടം ഒഴിവാക്കുന്നു, തൽക്ഷണ ഡാറ്റാ പങ്കിടൽ സാധ്യമാക്കുന്നു.
ഇതിനെക്കുറിച്ച് ഇങ്ങനെ ചിന്തിക്കുക:
postMessage()
ഉള്ള വെബ് വർക്കേഴ്സ്: ഇത് രണ്ട് സഹപ്രവർത്തകർ ഒരു ഡോക്യുമെന്റിന്റെ പകർപ്പുകൾ അങ്ങോട്ടും ഇങ്ങോട്ടും ഇമെയിൽ ചെയ്ത് ജോലി ചെയ്യുന്നതുപോലെയാണ്. ഓരോ മാറ്റത്തിനും ഒരു പുതിയ പകർപ്പ് അയക്കേണ്ടതുണ്ട്.SharedArrayBuffer
ഉള്ള വെബ് വർക്കേഴ്സ്: ഇത് രണ്ട് സഹപ്രവർത്തകർ ഒരു പങ്കിട്ട ഓൺലൈൻ എഡിറ്ററിൽ (ഗൂഗിൾ ഡോക്സ് പോലെ) ഒരേ ഡോക്യുമെന്റിൽ പ്രവർത്തിക്കുന്നതുപോലെയാണ്. മാറ്റങ്ങൾ രണ്ടുപേർക്കും തത്സമയം കാണാൻ കഴിയും.
പങ്കിട്ട മെമ്മറിയുടെ അപകടം: റേസ് കണ്ടീഷൻസ്
തൽക്ഷണ മെമ്മറി പങ്കിടൽ ശക്തമാണ്, പക്ഷേ ഇത് കൺകറന്റ് പ്രോഗ്രാമിംഗിന്റെ ലോകത്ത് നിന്നുള്ള ഒരു ക്ലാസിക് പ്രശ്നം അവതരിപ്പിക്കുന്നു: റേസ് കണ്ടീഷൻസ്.
ഒന്നിലധികം ത്രെഡുകൾ ഒരേ സമയം പങ്കിട്ട ഡാറ്റ ആക്സസ് ചെയ്യാനും പരിഷ്കരിക്കാനും ശ്രമിക്കുമ്പോഴാണ് ഒരു റേസ് കണ്ടീഷൻ ഉണ്ടാകുന്നത്, കൂടാതെ അന്തിമഫലം അവ എക്സിക്യൂട്ട് ചെയ്യുന്ന പ്രവചനാതീതമായ ക്രമത്തെ ആശ്രയിച്ചിരിക്കുന്നു. ഒരു `SharedArrayBuffer`-ൽ സംഭരിച്ചിരിക്കുന്ന ഒരു ലളിതമായ കൗണ്ടർ പരിഗണിക്കുക. മെയിൻ ത്രെഡിനും ഒരു വർക്കറിനും അത് വർദ്ധിപ്പിക്കണം.
- ത്രെഡ് A നിലവിലെ മൂല്യം, അതായത് 5, വായിക്കുന്നു.
- ത്രെഡ് A പുതിയ മൂല്യം എഴുതുന്നതിന് മുമ്പ്, ഓപ്പറേറ്റിംഗ് സിസ്റ്റം അതിനെ താൽക്കാലികമായി നിർത്തി ത്രെഡ് B-യിലേക്ക് മാറുന്നു.
- ത്രെഡ് B നിലവിലെ മൂല്യം വായിക്കുന്നു, അത് ഇപ്പോഴും 5 ആണ്.
- ത്രെഡ് B പുതിയ മൂല്യം (6) കണക്കാക്കുകയും അത് മെമ്മറിയിലേക്ക് തിരികെ എഴുതുകയും ചെയ്യുന്നു.
- സിസ്റ്റം ത്രെഡ് A-യിലേക്ക് തിരികെ വരുന്നു. ത്രെഡ് B എന്തെങ്കിലും ചെയ്തതായി അതിനറിയില്ല. അത് നിർത്തിയിടത്ത് നിന്ന് പുനരാരംഭിക്കുന്നു, അതിന്റെ പുതിയ മൂല്യം (5 + 1 = 6) കണക്കാക്കുകയും 6 മെമ്മറിയിലേക്ക് തിരികെ എഴുതുകയും ചെയ്യുന്നു.
കൗണ്ടർ രണ്ടുതവണ വർദ്ധിപ്പിച്ചെങ്കിലും, അവസാന മൂല്യം 6 ആണ്, 7 അല്ല. പ്രവർത്തനങ്ങൾ അറ്റോമിക് ആയിരുന്നില്ല - അവയെ തടസ്സപ്പെടുത്താമായിരുന്നു, ഇത് ഡാറ്റ നഷ്ടപ്പെടാൻ ഇടയാക്കി. ഇതുകൊണ്ടാണ് നിങ്ങൾക്ക് `SharedArrayBuffer` അതിന്റെ നിർണായക പങ്കാളിയായ `Atomics` ഒബ്ജക്റ്റ് ഇല്ലാതെ ഉപയോഗിക്കാൻ കഴിയാത്തത്.
പങ്കിട്ട മെമ്മറിയുടെ സംരക്ഷകൻ: `Atomics` ഒബ്ജക്റ്റ്
`Atomics` ഒബ്ജക്റ്റ്, `SharedArrayBuffer` ഒബ്ജക്റ്റുകളിൽ അറ്റോമിക് പ്രവർത്തനങ്ങൾ നടത്തുന്നതിനുള്ള ഒരു കൂട്ടം സ്റ്റാറ്റിക് മെത്തേഡുകൾ നൽകുന്നു. ഒരു അറ്റോമിക് പ്രവർത്തനം മറ്റൊരു പ്രവർത്തനത്താൽ തടസ്സപ്പെടാതെ പൂർണ്ണമായി നിർവഹിക്കപ്പെടുമെന്ന് ഉറപ്പുനൽകുന്നു. അത് ഒന്നുകിൽ പൂർണ്ണമായി സംഭവിക്കുന്നു അല്ലെങ്കിൽ സംഭവിക്കുന്നില്ല.
`Atomics` ഉപയോഗിക്കുന്നത്, പങ്കിട്ട മെമ്മറിയിലെ റീഡ്-മോഡിഫൈ-റൈറ്റ് പ്രവർത്തനങ്ങൾ സുരക്ഷിതമായി നടക്കുന്നുവെന്ന് ഉറപ്പാക്കുന്നതിലൂടെ റേസ് കണ്ടീഷനുകൾ തടയുന്നു.
പ്രധാന `Atomics` മെത്തേഡുകൾ
`Atomics` നൽകുന്ന ഏറ്റവും പ്രധാനപ്പെട്ട ചില മെത്തേഡുകൾ നോക്കാം.
Atomics.load(typedArray, index)
: ഒരു നിശ്ചിത ഇൻഡെക്സിലെ മൂല്യം അറ്റോമിക് ആയി വായിക്കുകയും അത് തിരികെ നൽകുകയും ചെയ്യുന്നു. നിങ്ങൾ പൂർണ്ണവും കേടുപാടുകൾ സംഭവിക്കാത്തതുമായ ഒരു മൂല്യമാണ് വായിക്കുന്നതെന്ന് ഇത് ഉറപ്പാക്കുന്നു.Atomics.store(typedArray, index, value)
: ഒരു നിശ്ചിത ഇൻഡെക്സിൽ ഒരു മൂല്യം അറ്റോമിക് ആയി സംഭരിക്കുകയും ആ മൂല്യം തിരികെ നൽകുകയും ചെയ്യുന്നു. എഴുതുന്ന പ്രവർത്തനം തടസ്സപ്പെടുന്നില്ലെന്ന് ഇത് ഉറപ്പാക്കുന്നു.Atomics.add(typedArray, index, value)
: ഒരു നിശ്ചിത ഇൻഡെക്സിലെ മൂല്യത്തിലേക്ക് ഒരു മൂല്യം അറ്റോമിക് ആയി ചേർക്കുന്നു. ഇത് ആ സ്ഥാനത്തെ യഥാർത്ഥ മൂല്യം തിരികെ നൽകുന്നു. ഇത്x += value
എന്നതിന്റെ അറ്റോമിക് തുല്യമാണ്.Atomics.sub(typedArray, index, value)
: ഒരു നിശ്ചിത ഇൻഡെക്സിലെ മൂല്യത്തിൽ നിന്ന് ഒരു മൂല്യം അറ്റോമിക് ആയി കുറയ്ക്കുന്നു.Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)
: ഇത് ഒരു ശക്തമായ കണ്ടീഷണൽ റൈറ്റ് ആണ്. ഇത്index
-ലെ മൂല്യംexpectedValue
-ന് തുല്യമാണോ എന്ന് പരിശോധിക്കുന്നു. അങ്ങനെയാണെങ്കിൽ, അത്replacementValue
ഉപയോഗിച്ച് മാറ്റിസ്ഥാപിക്കുകയും യഥാർത്ഥexpectedValue
തിരികെ നൽകുകയും ചെയ്യുന്നു. അല്ലെങ്കിൽ, അത് ഒന്നും ചെയ്യാതെ നിലവിലെ മൂല്യം തിരികെ നൽകുന്നു. ലോക്കുകൾ പോലുള്ള കൂടുതൽ സങ്കീർണ്ണമായ സിൻക്രൊണൈസേഷൻ പ്രിമിറ്റീവുകൾ നടപ്പിലാക്കുന്നതിനുള്ള ഒരു അടിസ്ഥാന ബിൽഡിംഗ് ബ്ലോക്കാണിത്.
സിൻക്രൊണൈസേഷൻ: ലളിതമായ പ്രവർത്തനങ്ങൾക്കപ്പുറം
ചിലപ്പോൾ നിങ്ങൾക്ക് സുരക്ഷിതമായ വായനയും എഴുത്തും എന്നതിലുപരിയായി കൂടുതൽ ആവശ്യമായി വരും. ത്രെഡുകൾക്ക് പരസ്പരം ഏകോപിപ്പിക്കുകയും കാത്തിരിക്കുകയും ചെയ്യേണ്ടതുണ്ട്. ഒരു സാധാരണ ആന്റി-പാറ്റേൺ 'ബിസി-വെയ്റ്റിംഗ്' ആണ്, അവിടെ ഒരു ത്രെഡ് ഒരു മാറ്റത്തിനായി ഒരു മെമ്മറി ലൊക്കേഷൻ നിരന്തരം പരിശോധിച്ചുകൊണ്ട് ഒരു ടൈറ്റ് ലൂപ്പിൽ ഇരിക്കുന്നു. ഇത് സിപിയു സൈക്കിളുകൾ പാഴാക്കുകയും ബാറ്ററി ലൈഫ് കുറയ്ക്കുകയും ചെയ്യുന്നു.
`Atomics` `wait()`, `notify()` എന്നിവ ഉപയോഗിച്ച് കൂടുതൽ കാര്യക്ഷമമായ ഒരു പരിഹാരം നൽകുന്നു.
Atomics.wait(typedArray, index, value, timeout)
: ഇത് ഒരു ത്രെഡിനോട് ഉറങ്ങാൻ പറയുന്നു. ഇത്index
-ലെ മൂല്യം ഇപ്പോഴുംvalue
ആണോ എന്ന് പരിശോധിക്കുന്നു. അങ്ങനെയാണെങ്കിൽ,Atomics.notify()
വഴി ഉണർത്തുന്നത് വരെ അല്ലെങ്കിൽ ഓപ്ഷണൽtimeout
(മില്ലിസെക്കൻഡിൽ) എത്തുന്നത് വരെ ത്രെഡ് ഉറങ്ങുന്നു.index
-ലെ മൂല്യം ഇതിനകം മാറിയിട്ടുണ്ടെങ്കിൽ, അത് ഉടനടി തിരികെ വരുന്നു. ഉറങ്ങുന്ന ഒരു ത്രെഡ് സിപിയു റിസോഴ്സുകൾ ഉപയോഗിക്കാത്തതിനാൽ ഇത് അവിശ്വസനീയമാംവിധം കാര്യക്ഷമമാണ്.Atomics.notify(typedArray, index, count)
: `Atomics.wait()` വഴി ഒരു പ്രത്യേക മെമ്മറി ലൊക്കേഷനിൽ ഉറങ്ങുന്ന ത്രെഡുകളെ ഉണർത്താൻ ഇത് ഉപയോഗിക്കുന്നു. ഇത് പരമാവധിcount
കാത്തിരിക്കുന്ന ത്രെഡുകളെ ഉണർത്തും (അല്ലെങ്കിൽcount
നൽകിയിട്ടില്ലെങ്കിലോInfinity
ആണെങ്കിലോ എല്ലാവരെയും).
എല്ലാം ഒരുമിച്ച് ചേർക്കുന്നു: ഒരു പ്രായോഗിക ഗൈഡ്
ഇപ്പോൾ നമ്മൾ സിദ്ധാന്തം മനസ്സിലാക്കി, `SharedArrayBuffer` ഉപയോഗിച്ച് ഒരു പരിഹാരം നടപ്പിലാക്കുന്നതിനുള്ള ഘട്ടങ്ങളിലൂടെ നമുക്ക് പോകാം.
ഘട്ടം 1: സുരക്ഷാ മുൻവ്യവസ്ഥ - ക്രോസ്-ഒറിജിൻ ഐസൊലേഷൻ
ഡെവലപ്പർമാർക്കുള്ള ഏറ്റവും സാധാരണമായ ഒരു തടസ്സമാണിത്. സുരക്ഷാ കാരണങ്ങളാൽ, `SharedArrayBuffer` ഒരു ക്രോസ്-ഒറിജിൻ ഐസൊലേറ്റഡ് അവസ്ഥയിലുള്ള പേജുകളിൽ മാത്രമേ ലഭ്യമാകൂ. സ്പെക്ടർ പോലുള്ള സ്പെകുലേറ്റീവ് എക്സിക്യൂഷൻ കേടുപാടുകൾ ലഘൂകരിക്കുന്നതിനുള്ള ഒരു സുരക്ഷാ നടപടിയാണിത്, ഇത് ഒറിജിനുകൾക്കിടയിൽ ഡാറ്റ ചോർത്തുന്നതിന് ഉയർന്ന റെസല്യൂഷൻ ടൈമറുകൾ (ഷെയർഡ് മെമ്മറിയിലൂടെ സാധ്യമാക്കിയത്) ഉപയോഗിക്കാൻ സാധ്യതയുണ്ട്.
ക്രോസ്-ഒറിജിൻ ഐസൊലേഷൻ പ്രവർത്തനക്ഷമമാക്കുന്നതിന്, നിങ്ങളുടെ പ്രധാന ഡോക്യുമെന്റിനായി രണ്ട് നിർദ്ദിഷ്ട എച്ച്ടിടിപി ഹെഡ്ഡറുകൾ അയയ്ക്കാൻ നിങ്ങളുടെ വെബ് സെർവർ കോൺഫിഗർ ചെയ്യണം:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
(COOP): നിങ്ങളുടെ ഡോക്യുമെന്റിന്റെ ബ്രൗസിംഗ് കോൺടെക്സ്റ്റിനെ മറ്റ് ഡോക്യുമെന്റുകളിൽ നിന്ന് വേർതിരിക്കുന്നു, നിങ്ങളുടെ വിൻഡോ ഒബ്ജക്റ്റുമായി നേരിട്ട് സംവദിക്കുന്നതിൽ നിന്ന് അവയെ തടയുന്നു.Cross-Origin-Embedder-Policy: require-corp
(COEP): നിങ്ങളുടെ പേജ് ലോഡുചെയ്യുന്ന എല്ലാ സബ് റിസോഴ്സുകളും (ചിത്രങ്ങൾ, സ്ക്രിപ്റ്റുകൾ, ഐഫ്രെയിമുകൾ പോലുള്ളവ) ഒരേ ഒറിജിനിൽ നിന്നുള്ളതായിരിക്കണം അല്ലെങ്കിൽCross-Origin-Resource-Policy
ഹെഡ്ഡർ അല്ലെങ്കിൽ CORS ഉപയോഗിച്ച് ക്രോസ്-ഒറിജിൻ ലോഡബിൾ ആയി വ്യക്തമായി അടയാളപ്പെടുത്തിയിരിക്കണം എന്ന് ആവശ്യപ്പെടുന്നു.
ഇത് സജ്ജീകരിക്കാൻ വെല്ലുവിളിയാകാം, പ്രത്യേകിച്ചും നിങ്ങൾ ആവശ്യമായ ഹെഡ്ഡറുകൾ നൽകാത്ത മൂന്നാം കക്ഷി സ്ക്രിപ്റ്റുകളെയോ ഉറവിടങ്ങളെയോ ആശ്രയിക്കുന്നുവെങ്കിൽ. നിങ്ങളുടെ സെർവർ കോൺഫിഗർ ചെയ്ത ശേഷം, ബ്രൗസറിന്റെ കൺസോളിൽ self.crossOriginIsolated
പ്രോപ്പർട്ടി പരിശോധിച്ച് നിങ്ങളുടെ പേജ് ഐസൊലേറ്റഡ് ആണോ എന്ന് പരിശോധിക്കാൻ കഴിയും. അത് true
ആയിരിക്കണം.
ഘട്ടം 2: ബഫർ ഉണ്ടാക്കുകയും പങ്കുവെക്കുകയും ചെയ്യുക
നിങ്ങളുടെ പ്രധാന സ്ക്രിപ്റ്റിൽ, നിങ്ങൾ `SharedArrayBuffer`-ഉം അതിൽ `Int32Array` പോലുള്ള ഒരു `TypedArray` ഉപയോഗിച്ച് ഒരു 'വ്യൂ'-വും ഉണ്ടാക്കുന്നു.
main.js:
// ആദ്യം ക്രോസ്-ഒറിജിൻ ഐസൊലേഷൻ പരിശോധിക്കുക!
if (!self.crossOriginIsolated) {
console.error("ഈ പേജ് ക്രോസ്-ഒറിജിൻ ഐസൊലേറ്റഡ് അല്ല. SharedArrayBuffer ലഭ്യമാകില്ല.");
} else {
// ഒരു 32-ബിറ്റ് പൂർണ്ണസംഖ്യയ്ക്കായി ഒരു ഷെയർഡ് ബഫർ ഉണ്ടാക്കുക.
const buffer = new SharedArrayBuffer(4);
// ബഫറിൽ ഒരു വ്യൂ ഉണ്ടാക്കുക. എല്ലാ അറ്റോമിക് പ്രവർത്തനങ്ങളും വ്യൂവിലാണ് നടക്കുന്നത്.
const int32Array = new Int32Array(buffer);
// ഇൻഡെക്സ് 0-ലെ മൂല്യം ഇനീഷ്യലൈസ് ചെയ്യുക.
int32Array[0] = 0;
// ഒരു പുതിയ വർക്കർ ഉണ്ടാക്കുക.
const worker = new Worker('worker.js');
// ഷെയർഡ് ബഫർ വർക്കറിലേക്ക് അയക്കുക. ഇതൊരു റഫറൻസ് കൈമാറ്റമാണ്, പകർപ്പല്ല.
worker.postMessage({ buffer });
// വർക്കറിൽ നിന്നുള്ള സന്ദേശങ്ങൾക്കായി കാത്തിരിക്കുക.
worker.onmessage = (event) => {
console.log(`വർക്കർ പൂർത്തിയായതായി റിപ്പോർട്ട് ചെയ്തു. അവസാന മൂല്യം: ${Atomics.load(int32Array, 0)}`);
};
}
ഘട്ടം 3: വർക്കറിൽ അറ്റോമിക് ഓപ്പറേഷൻസ് നടത്തുന്നു
വർക്കർ ബഫർ സ്വീകരിക്കുകയും ഇപ്പോൾ അതിൽ അറ്റോമിക് പ്രവർത്തനങ്ങൾ നടത്തുകയും ചെയ്യാം.
worker.js:
self.onmessage = (event) => {
const { buffer } = event.data;
const int32Array = new Int32Array(buffer);
console.log("വർക്കറിന് ഷെയർഡ് ബഫർ ലഭിച്ചു.");
// നമുക്ക് ചില അറ്റോമിക് പ്രവർത്തനങ്ങൾ നടത്താം.
for (let i = 0; i < 1000000; i++) {
// പങ്കിട്ട മൂല്യം സുരക്ഷിതമായി വർദ്ധിപ്പിക്കുക.
Atomics.add(int32Array, 0, 1);
}
console.log("വർക്കർ വർദ്ധിപ്പിക്കൽ പൂർത്തിയാക്കി.");
// നമ്മൾ പൂർത്തിയാക്കിയെന്ന് മെയിൻ ത്രെഡിന് സിഗ്നൽ നൽകുക.
self.postMessage({ done: true });
};
ഘട്ടം 4: കൂടുതൽ വിപുലമായ ഒരു ഉദാഹരണം - സിൻക്രൊണൈസേഷനോടുകൂടിയ പാരലൽ സമ്മേഷൻ
കൂടുതൽ യാഥാർത്ഥ്യബോധമുള്ള ഒരു പ്രശ്നം നമുക്ക് കൈകാര്യം ചെയ്യാം: ഒന്നിലധികം വർക്കറുകൾ ഉപയോഗിച്ച് ഒരു വലിയ സംഖ്യകളുടെ അറേയുടെ തുക കാണുക. കാര്യക്ഷമമായ സിൻക്രൊണൈസേഷനായി നമ്മൾ `Atomics.wait()`, `Atomics.notify()` എന്നിവ ഉപയോഗിക്കും.
നമ്മുടെ ഷെയർഡ് ബഫറിന് മൂന്ന് ഭാഗങ്ങളുണ്ടാകും:
- ഇൻഡെക്സ് 0: ഒരു സ്റ്റാറ്റസ് ഫ്ലാഗ് (0 = പ്രോസസ്സിംഗിൽ, 1 = പൂർത്തിയായി).
- ഇൻഡെക്സ് 1: എത്ര വർക്കറുകൾ പൂർത്തിയാക്കി എന്നതിനുള്ള ഒരു കൗണ്ടർ.
- ഇൻഡെക്സ് 2: അവസാനത്തെ തുക.
main.js:
if (self.crossOriginIsolated) {
const NUM_WORKERS = 4;
const DATA_SIZE = 10_000_000;
// [സ്റ്റാറ്റസ്, വർക്കറുകൾ_പൂർത്തിയായി, ഫലം_ലോ, ഫലം_ഹൈ]
// വലിയ തുകകൾക്ക് ഓവർഫ്ലോ ഒഴിവാക്കാൻ നമ്മൾ ഫലത്തിനായി രണ്ട് 32-ബിറ്റ് പൂർണ്ണസംഖ്യകൾ ഉപയോഗിക്കുന്നു.
const sharedBuffer = new SharedArrayBuffer(4 * 4); // 4 പൂർണ്ണസംഖ്യകൾ
const sharedArray = new Int32Array(sharedBuffer);
// പ്രോസസ്സ് ചെയ്യാൻ കുറച്ച് റാൻഡം ഡാറ്റ ഉണ്ടാക്കുക
const data = new Uint8Array(DATA_SIZE);
for (let i = 0; i < DATA_SIZE; i++) {
data[i] = Math.floor(Math.random() * 10);
}
const chunkSize = Math.ceil(DATA_SIZE / NUM_WORKERS);
for (let i = 0; i < NUM_WORKERS; i++) {
const worker = new Worker('sum_worker.js');
const start = i * chunkSize;
const end = Math.min(start + chunkSize, DATA_SIZE);
// വർക്കറിന്റെ ഡാറ്റാ ചങ്കിനായി ഒരു നോൺ-ഷെയർഡ് വ്യൂ ഉണ്ടാക്കുക
const dataChunk = data.subarray(start, end);
worker.postMessage({
sharedBuffer,
dataChunk // ഇത് പകർത്തുന്നു
});
}
console.log('മെയിൻ ത്രെഡ് ഇപ്പോൾ വർക്കറുകൾ പൂർത്തിയാക്കാൻ കാത്തിരിക്കുകയാണ്...');
// ഇൻഡെക്സ് 0-ലെ സ്റ്റാറ്റസ് ഫ്ലാഗ് 1 ആകാൻ കാത്തിരിക്കുക
// ഇത് ഒരു while ലൂപ്പിനേക്കാൾ വളരെ മികച്ചതാണ്!
Atomics.wait(sharedArray, 0, 0); // sharedArray[0] എന്നത് 0 ആണെങ്കിൽ കാത്തിരിക്കുക
console.log('മെയിൻ ത്രെഡ് ഉണർന്നു!');
const finalSum = Atomics.load(sharedArray, 2);
console.log(`അവസാനത്തെ പാരലൽ തുക: ${finalSum}`);
} else {
console.error('പേജ് ക്രോസ്-ഒറിജിൻ ഐസൊലേറ്റഡ് അല്ല.');
}
sum_worker.js:
self.onmessage = ({ data }) => {
const { sharedBuffer, dataChunk } = data;
const sharedArray = new Int32Array(sharedBuffer);
// ഈ വർക്കറിന്റെ ചങ്കിനുള്ള തുക കണക്കാക്കുക
let localSum = 0;
for (let i = 0; i < dataChunk.length; i++) {
localSum += dataChunk[i];
}
// ലോക്കൽ തുക ഷെയർഡ് ടോട്ടലിലേക്ക് അറ്റോമിക് ആയി ചേർക്കുക
Atomics.add(sharedArray, 2, localSum);
// 'വർക്കറുകൾ പൂർത്തിയായി' എന്ന കൗണ്ടർ അറ്റോമിക് ആയി വർദ്ധിപ്പിക്കുക
const finishedCount = Atomics.add(sharedArray, 1, 1) + 1;
// പൂർത്തിയാക്കുന്ന അവസാനത്തെ വർക്കർ ഇതാണെങ്കിൽ...
const NUM_WORKERS = 4; // ഒരു യഥാർത്ഥ ആപ്പിൽ ഇത് പാസ് ചെയ്യണം
if (finishedCount === NUM_WORKERS) {
console.log('അവസാനത്തെ വർക്കർ പൂർത്തിയാക്കി. മെയിൻ ത്രെഡിനെ അറിയിക്കുന്നു.');
// 1. സ്റ്റാറ്റസ് ഫ്ലാഗ് 1 (പൂർത്തിയായി) ആയി സജ്ജമാക്കുക
Atomics.store(sharedArray, 0, 1);
// 2. ഇൻഡെക്സ് 0-ൽ കാത്തിരിക്കുന്ന മെയിൻ ത്രെഡിനെ അറിയിക്കുക
Atomics.notify(sharedArray, 0, 1);
}
};
യഥാർത്ഥ ലോക ഉപയോഗങ്ങളും പ്രയോഗങ്ങളും
ശക്തവും എന്നാൽ സങ്കീർണ്ണവുമായ ഈ സാങ്കേതികവിദ്യ യഥാർത്ഥത്തിൽ എവിടെയാണ് ഒരു മാറ്റം വരുത്തുന്നത്? വലിയ ഡാറ്റാസെറ്റുകളിൽ ഭാരമേറിയതും സമാന്തരമാക്കാൻ കഴിയുന്നതുമായ കമ്പ്യൂട്ടേഷൻ ആവശ്യമായ ആപ്ലിക്കേഷനുകളിൽ ഇത് മികവ് പുലർത്തുന്നു.
- വെബ്അസെംബ്ലി (Wasm): ഇതാണ് ഏറ്റവും പ്രധാനപ്പെട്ട ഉപയോഗം. C++, Rust, Go തുടങ്ങിയ ഭാഷകൾക്ക് മൾട്ടിത്രെഡിംഗിന് മികച്ച പിന്തുണയുണ്ട്. നിലവിലുള്ള ഉയർന്ന പ്രകടനമുള്ള, മൾട്ടി-ത്രെഡഡ് ആപ്ലിക്കേഷനുകൾ (ഗെയിം എഞ്ചിനുകൾ, CAD സോഫ്റ്റ്വെയർ, ശാസ്ത്രീയ മോഡലുകൾ എന്നിവ പോലുള്ളവ) ബ്രൗസറിൽ പ്രവർത്തിപ്പിക്കാൻ Wasm ഡെവലപ്പർമാരെ അനുവദിക്കുന്നു, ത്രെഡ് ആശയവിനിമയത്തിനുള്ള അടിസ്ഥാന സംവിധാനമായി `SharedArrayBuffer` ഉപയോഗിക്കുന്നു.
- ഇൻ-ബ്രൗസർ ഡാറ്റാ പ്രോസസ്സിംഗ്: വലിയ തോതിലുള്ള ഡാറ്റാ വിഷ്വലൈസേഷൻ, ക്ലയന്റ്-സൈഡ് മെഷീൻ ലേണിംഗ് മോഡൽ ഇൻഫറൻസ്, വലിയ അളവിലുള്ള ഡാറ്റ പ്രോസസ്സ് ചെയ്യുന്ന ശാസ്ത്രീയ സിമുലേഷനുകൾ എന്നിവയെല്ലാം കാര്യമായി വേഗത്തിലാക്കാൻ കഴിയും.
- മീഡിയ എഡിറ്റിംഗ്: ഉയർന്ന റെസല്യൂഷനുള്ള ചിത്രങ്ങളിൽ ഫിൽട്ടറുകൾ പ്രയോഗിക്കുന്നതും അല്ലെങ്കിൽ ഒരു സൗണ്ട് ഫയലിൽ ഓഡിയോ പ്രോസസ്സിംഗ് നടത്തുന്നതും കഷണങ്ങളായി വിഭജിച്ച് ഒന്നിലധികം വർക്കറുകൾ സമാന്തരമായി പ്രോസസ്സ് ചെയ്യാം, ഇത് ഉപയോക്താവിന് തത്സമയ ഫീഡ്ബാക്ക് നൽകുന്നു.
- ഉയർന്ന പ്രകടനമുള്ള ഗെയിമിംഗ്: ആധുനിക ഗെയിം എഞ്ചിനുകൾ ഫിസിക്സ്, എഐ, അസറ്റ് ലോഡിംഗ് എന്നിവയ്ക്കായി മൾട്ടിത്രെഡിംഗിനെ വളരെയധികം ആശ്രയിക്കുന്നു. ബ്രൗസറിൽ പൂർണ്ണമായും പ്രവർത്തിക്കുന്ന കൺസോൾ-നിലവാരത്തിലുള്ള ഗെയിമുകൾ നിർമ്മിക്കാൻ `SharedArrayBuffer` സാധ്യമാക്കുന്നു.
വെല്ലുവിളികളും അന്തിമ പരിഗണനകളും
`SharedArrayBuffer` പരിവർത്തനാത്മകമാണെങ്കിലും, അതൊരു ഒറ്റമൂലിയല്ല. ശ്രദ്ധയോടെ കൈകാര്യം ചെയ്യേണ്ട ഒരു ലോ-ലെവൽ ടൂൾ ആണിത്.
- സങ്കീർണ്ണത: കൺകറന്റ് പ്രോഗ്രാമിംഗ് വളരെ ബുദ്ധിമുട്ടുള്ള ഒന്നാണ്. റേസ് കണ്ടീഷനുകളും ഡെഡ്ലോക്കുകളും ഡീബഗ് ചെയ്യുന്നത് അവിശ്വസനീയമാംവിധം വെല്ലുവിളി നിറഞ്ഞതാണ്. നിങ്ങളുടെ ആപ്ലിക്കേഷൻ സ്റ്റേറ്റ് എങ്ങനെ കൈകാര്യം ചെയ്യുന്നു എന്നതിനെക്കുറിച്ച് നിങ്ങൾ വ്യത്യസ്തമായി ചിന്തിക്കണം.
- ഡെഡ്ലോക്കുകൾ: രണ്ടോ അതിലധികമോ ത്രെഡുകൾ എന്നെന്നേക്കുമായി തടസ്സപ്പെടുമ്പോഴാണ് ഒരു ഡെഡ്ലോക്ക് സംഭവിക്കുന്നത്, ഓരോന്നും മറ്റൊന്ന് ഒരു റിസോഴ്സ് റിലീസ് ചെയ്യാൻ കാത്തിരിക്കുന്നു. നിങ്ങൾ സങ്കീർണ്ണമായ ലോക്കിംഗ് മെക്കാനിസങ്ങൾ തെറ്റായി നടപ്പിലാക്കിയാൽ ഇത് സംഭവിക്കാം.
- സുരക്ഷാ അധികച്ചെലവ്: ക്രോസ്-ഒറിജിൻ ഐസൊലേഷൻ ആവശ്യകത ഒരു പ്രധാന തടസ്സമാണ്. ആവശ്യമായ CORS/CORP ഹെഡ്ഡറുകൾ പിന്തുണയ്ക്കുന്നില്ലെങ്കിൽ മൂന്നാം കക്ഷി സേവനങ്ങൾ, പരസ്യങ്ങൾ, പേയ്മെന്റ് ഗേറ്റ്വേകൾ എന്നിവയുമായുള്ള സംയോജനം ഇത് തകർക്കും.
- എല്ലാ പ്രശ്നങ്ങൾക്കുമുള്ളതല്ല: ലളിതമായ പശ്ചാത്തല ജോലികൾക്കോ I/O പ്രവർത്തനങ്ങൾക്കോ, `postMessage()` ഉള്ള പരമ്പരാഗത വെബ് വർക്കർ മോഡൽ പലപ്പോഴും ലളിതവും പര്യാപ്തവുമാണ്. വലിയ അളവിലുള്ള ഡാറ്റ ഉൾപ്പെടുന്ന വ്യക്തമായ, സിപിയു-ബൗണ്ട് തടസ്സമുള്ളപ്പോൾ മാത്രം `SharedArrayBuffer` ഉപയോഗിക്കുക.
ഉപസംഹാരം
`SharedArrayBuffer`, `Atomics`, വെബ് വർക്കേഴ്സ് എന്നിവയുമായി ചേർന്ന് വെബ് ഡെവലപ്മെന്റിന് ഒരു മാതൃകാപരമായ മാറ്റത്തെ പ്രതിനിധീകരിക്കുന്നു. ഇത് സിംഗിൾ-ത്രെഡഡ് മോഡലിന്റെ അതിരുകൾ തകർക്കുന്നു, ശക്തവും പ്രകടനക്ഷമവും സങ്കീർണ്ണവുമായ ഒരു പുതിയ ക്ലാസ് ആപ്ലിക്കേഷനുകളെ ബ്രൗസറിലേക്ക് ക്ഷണിക്കുന്നു. കമ്പ്യൂട്ടേഷണൽ ഭാരമേറിയ ജോലികൾക്കായി വെബ് പ്ലാറ്റ്ഫോമിനെ നേറ്റീവ് ആപ്ലിക്കേഷൻ ഡെവലപ്മെന്റുമായി കൂടുതൽ തുല്യമായ നിലയിലേക്ക് ഇത് എത്തിക്കുന്നു.
കൺകറന്റ് ജാവാസ്ക്രിപ്റ്റിലേക്കുള്ള യാത്ര വെല്ലുവിളി നിറഞ്ഞതാണ്, സ്റ്റേറ്റ് മാനേജ്മെന്റ്, സിൻക്രൊണൈസേഷൻ, സുരക്ഷ എന്നിവയിൽ കർശനമായ സമീപനം ആവശ്യപ്പെടുന്നു. എന്നാൽ വെബിൽ സാധ്യമായതിന്റെ പരിധികൾ ലംഘിക്കാൻ ആഗ്രഹിക്കുന്ന ഡെവലപ്പർമാർക്ക് - തത്സമയ ഓഡിയോ സിന്തസിസ് മുതൽ സങ്കീർണ്ണമായ 3D റെൻഡറിംഗും ശാസ്ത്രീയ കമ്പ്യൂട്ടിംഗും വരെ - `SharedArrayBuffer` മാസ്റ്റർ ചെയ്യുന്നത് ഇനി ഒരു ഓപ്ഷൻ മാത്രമല്ല; വെബ് ആപ്ലിക്കേഷനുകളുടെ അടുത്ത തലമുറ കെട്ടിപ്പടുക്കുന്നതിനുള്ള ഒരു അവശ്യ വൈദഗ്ധ്യമാണിത്.