Module futures_signals :: آموزش

ساخت وبلاگ

این آموزش طولانی است ، اما در نظر گرفته شده است تا همه چیزهایی را که باید بدانید برای استفاده از سیگنال ها توضیح دهید.

خواندن همه آن بسیار توصیه می شود.

قابل تغییر

قبل از اینکه بتوانم سیگنال ها را به طور کامل توضیح دهم ، ابتدا باید تغییر پذیر را توضیح دهم:

مثال فوق با مقدار اولیه 5 قابل تغییر است.

قابل تغییر بسیار شبیه به ARC + RWLOCK است:

  • ارسال و همگام سازی را پیاده سازی می کند ، بنابراین می توان آن را بین چندین موضوع ارسال کرد و استفاده کرد.
  • این کلون را پیاده سازی می کند ، که مرجع جدیدی را برای همان تغییر پذیر (دقیقاً مانند قوس) ایجاد می کند.
  • می توانید مقدار فعلی را بازیابی کنید.
  • می توانید مقدار فعلی را تغییر دهید.

بیایید آن را در عمل ببینیم:

برای هر

با این حال ، اگر این همه قابل تغییر بود ، می توانست انجام دهد ، بسیار مفید نخواهد بود ، زیرا Rwlock قبلاً وجود دارد!

تفاوت عمده بین قابل تغییر و RWLOCK در این است که هر زمان که قابل تغییر باشد ، می توان به طور مؤثر مطلع شد:

اینگونه است که روش for_each کار می کند:

هنگامی که آن آینده تخم ریزی می شود ، آن را | مقدار |<.>بسته شدن با مقدار فعلی my_state (که در این حالت 10 است).

سپس هر زمان که my_state تغییر کند ، دوباره با مقدار جدید بسته می شود.

این می تواند برای انواع موارد استفاده شود ، مانند به روزرسانی خودکار GUI یا پایگاه داده خود هر زمان که سیگنال تغییر کند.

دقیقاً مانند آینده و جریان ، وقتی سیگنال ایجاد می کنید ، تا زمانی که تخم ریزی نشود ، در واقع کاری انجام نمی دهد.

به منظور ایجاد سیگنال ، ابتدا از روش for_each (همانطور که در بالا نشان داده شده است) برای تبدیل آن به آینده استفاده می کنید ، و سپس آن آینده را تخم ریزی می کنید.

روش های زیادی برای تخم ریزی آینده وجود دارد:

و خیلی بیشتر! از آنجا که for_each آینده ای عادی را برمی گرداند ، هر چیزی که می تواند آینده ای را ایجاد کند ، می تواند سیگنالی را نیز ایجاد کند.

این همچنین بدان معنی است که شما می توانید از تمام روش های FutureExt روی آن نیز استفاده کنید.

در جریان

اگر به کنترل بیشتری نیاز دارید ، می توانید به جای آن از to_stream استفاده کنید:

این یک جریان از مقادیر را برمی گرداند (با شروع مقدار فعلی my_state ، و سپس تغییرات در my_state).

شما می توانید از تمام روش های Streamext روی آن استفاده کنید ، دقیقاً مانند هر جریان دیگر.

علامت

ممکن است تعجب کنید که چرا باید با روش سیگنال () تماس بگیرید: چرا نمی توانید مستقیماً از تغییر مسیر استفاده کنید؟

در واقع سه روش مختلف وجود دارد که بسته به نیاز خود می توانید از آنها استفاده کنید:

سیگنال () راحت ترین است اما به مقدار کپی نیاز دارد.

SIGNAL_CLONED () به کلون نیاز دارد.

SIGNAL_REF (| x |<.>) بسته شدن A و مرجع به مقدار را ارائه می دهد ، بنابراین مقدار لازم نیست کپی یا کلون باشد ، بلکه در عوض مقدار با بسته شدن تعیین می شود.

این انعطاف پذیر ترین و همچنین سریعترین است ، اما طولانی ترین و دست و پا گیر ترین استفاده است.

علاوه بر این ، می توان چندین بار با روشهای سیگنال تماس گرفت (و آنها را مخلوط کنید):

هنگامی که تغییر قابل تغییر است ، به تمام سیگنال های آن اطلاع داده می شود.

این امر در عمل بسیار مفید است: معمول است که وضعیت برنامه خود را در داخل یک جهش جهانی (یا چندین تغییر پذیر) قرار دهید و سپس آن را در مکان های مختلف در طول برنامه خود به اشتراک بگذارید.

سرانجام ، تفاوت زیادی بین قابل تغییر و سیگنال وجود دارد: یک سیگنال فقط در صورت تغییر مقدار آن می تواند اطلاع داده شود. با این حال ، یک تغییر پذیر می تواند کارهای بیشتری را انجام دهد ، زیرا می تواند مقدار فعلی را بازیابی کند و همچنین می تواند مقدار را تنظیم کند.

علامت

اکنون که من کاملاً قابل توضیح را توضیح داده ام ، در نهایت می توانم سیگنال را توضیح دهم.

یک سیگنال یک مقدار کارآمد صفر است که با گذشت زمان تغییر می کند و در صورت تغییر می توانید به طور موثری مطلع شوید.

درست مانند آینده و جریان ، سیگنال ها در داخل دستگاه حالت بسیار کارآمد قرار می گیرند. بیشتر اوقات آنها کاملاً پشته هستند (بدون تخصیص پشته).

و در موارد نادری که آنها به آنها اختصاص می دهند ، فقط یک بار این کار را انجام می دهند ، هنگامی که سیگنال ایجاد می شود ، نه در حالی که سیگنال در حال اجرا است. این بدان معنی است که عملکرد بسیار عالی است ، حتی با میلیون ها سیگنال S به طور همزمان اجرا می شود.

درست مانند Futurext و Streamext ، ویژگی Signalext روشهای مفیدی دارد و بیشتر آنها سیگنال را برمی گردانند تا بتوانند زنجیر شوند:

بیایید بگوییم که مقدار فعلی my_state 10 است.

هنگامی که خروجی تخم ریزی می شود ، با | مقدار | تماس می گیردبسته شدن مقدار + 5 با مقدار فعلی my_value (بسته شدن 10 + 5 ، که 15 است).

سپس با DO_SOME_ASYNC_CACCULATION (15) که آینده را برمی گرداند ، تماس می گیرد. پس از اتمام این آینده ، DEDUPE بررسی می کند که آیا مقدار بازگشت با مقدار قبلی (با استفاده از ==) متفاوت است ، و اگر چنین است ، با مقدار جدید خروجی را اعلام می کند.

هر زمان که my_state تغییر کند ، این روند را به طور خودکار تکرار می کند ، اطمینان حاصل می کند که خروجی همیشه با my_state همگام سازی می شود.

همچنین می توان از MAP_REF استفاده کرد که شبیه به نقشه است به جز این که به شما امکان می دهد از چندین سیگنال ورودی استفاده کنید:

در این حالت MAP_REF به سه سیگنال S بستگی دارد: FOO ، BAR و QUX.

هر زمان که هر یک از این سیگنال ها تغییر کنند ، کد *foo + *bar + *qux را مجدداً مورد استفاده قرار می دهد.

این بدان معنی است که خروجی همیشه برابر با جمع FOO ، BAR و QUX خواهد بود.

سیگنال ها ضرر دارند

این مهم است که درک کنیم که for_each ، to_stream و سایر روش های سیگنال متن از بین می روند: ممکن است از تغییرات استفاده کنند.

دلیلش این است که آنها فقط به جدیدترین ارزش اهمیت می دهند. بنابراین اگر مقدار در مدت زمان کوتاه چندین بار تغییر کند ، فقط جدیدترین تغییر را تشخیص می دهد.

به عنوان مثال:

در این حالت فقط 3 تغییر را تشخیص می دهد. تغییر 2 کاملاً نادیده گرفته می شود ، مثل اینکه انگار هرگز اتفاق نیفتاده است.

این یک انتخاب طراحی عمدی است: برای صحت و عملکرد لازم است.

بنابراین هر زمان که از سیگنال استفاده می کنید ، نباید به آن اعتماد کنید که برای مقادیر متوسط به روز می شود.

این ممکن است مانند یک مشکل به نظر برسد ، اما در واقع به هیچ وجه مشکلی نیست: سیگنال S تضمین می شود که همیشه با جدیدترین مقدار به روز شود ، بنابراین فقط مقادیر واسطه ای است که تضمین نمی شوند.

این شبیه به RWLOCK است ، که به شما امکان دسترسی به مقادیر گذشته (فقط مقدار فعلی) را نمی دهد. بنابراین تا زمانی که برنامه شما فقط به جدیدترین مقدار اهمیت می دهد ، سیگنال S کاملاً کار خواهد کرد.

اگر واقعاً به تمام مقادیر میانی (نه فقط جدیدترین) نیاز دارید ، پس استفاده از یک جریان (مانند آینده :: کانال :: MPSC :: Unbounded) یک انتخاب عالی خواهد بود. در این حالت شما یک مجازات عملکردی کوچک را پرداخت خواهید کرد ، زیرا باید مقادیر را در یک صف نگه دارد.

و البته شما می توانید با استفاده از هرکدام در جایی که مناسب باشد ، آزادانه S ، Stream S و Signal S را در برنامه خود مخلوط کنید.

دارای VableVec

علاوه بر جهش و سیگنال ، همچنین قابل تغییر ، سیگنال VEC و SignalVext نیز وجود دارد.

به عنوان مثال:

موارد فوق یک Valibe VableVec جدید ایجاد می کند.

سپس می توانید از lock_mut استفاده کنید که قفل را برمی گرداند. همانطور که از نام آن پیداست ، در حالی که شما قفل را در دست دارید ، دسترسی قابل احتراق منحصر به فرد به MutableVec دارید.

قفل شامل بسیاری از روشهای VEC است:

روش های درج نیاز به کپی دارند ، اما انواع _cloned نیز وجود دارد که به جای آن به کلون نیاز دارند:

این مورد نیاز است زیرا وقتی مقدار جدیدی را وارد می کنید ، مجبور است یک نسخه را به تمام سیگنال های S که در حال گوش دادن به تغییرات هستند ، ارسال کند.

این قفل همچنین دارای اجرای DEREF برای & [t] است ، بنابراین می توانید از تمام روشهای برش غیرقابل تغییر در آن استفاده کنید:

در آخر ، می توانید از روشهای سیگنال_اك () یا سیگنال_اكا_كلون () برای تبدیل آن به یك سیگنال استفاده كنید ، و سپس می توانید از روش for_each استفاده كنید تا هنگام تغییر ، به طور كارآیی مطلع شوید:

درست مانند Signalext :: for_each ، SignalVeCext :: for_each روش آینده را برمی گرداند.

وقتی این آینده تخم ریزی می شود:

اگر سیگنال VEC از قبل دارای مقادیر باشد ، بسته شدن آن را با Vecdiff :: جایگزین می کند ، که حاوی مقادیر فعلی برای سیگنال VEC است.

اگر SignalVec هیچ مقداری ندارد ، بسته شدن آن را صدا نمی کند.

هر وقت سیگنال VEC تغییر می کند ، بسته شدن آن را با Vecdiff برای تغییر فراخوانی می کند.

بر خلاف Signalext :: for_each ، SignalVeCext :: for_each بسته بندی را با یک Vecdiff فراخوانی می کند ، که حاوی تفاوت بین VEC جدید و VEC قدیمی است.

به عنوان نمونه ، اگر با قفل تماس بگیرید. پوش (5) ، بسته شدن آن با Vecdiff :: PUSH فراخوانی می شود

و اگر با Lock. Insert (3 ، 10) تماس بگیرید ، بسته شدن با Vecdiff :: درج فراخوانی می شود

این به شما امکان می دهد تا فقط بر اساس آن تغییر خاص ، بسیار کارآمد را به روز کنید.

به عنوان مثال ، اگر هر زمان که تغییر کند ، به طور خودکار در حال ذخیره سازی VableVec در یک پایگاه داده ذخیره می کنید ، نیازی نیست که هنگام تغییر ، کل MutableVec را ذخیره کنید ، فقط باید تغییرات فردی را ذخیره کنید. این بدان معناست که غالباً O (1) زمان ثابت خواهد بود ، مهم نیست که MutableVec چقدر بزرگ باشد.

سیگنال

درست مانند Signalext ، SignalVeCext روشهای مفیدی دارد و بیشتر آنها یک سیگنال را برمی گردانند تا بتوانند زنجیر شوند:

آنها به طور کلی بسیار کارآمد هستند (به عنوان مثال MAP زمان ثابت است ، مهم نیست که سیگنال VEC چقدر بزرگ باشد و فیلتر زمان خطی باشد).

SignalVec بدون ضرر است

بر خلاف سیگنال ، تضمین شده است که SignalVec هرگز تغییری را از دست نخواهد داد. علاوه بر این ، تغییرات همیشه به ترتیب صحیح خواهند بود.

این امر به این دلیل است که با تفاوت بین VEC قدیمی و VEC جدید آگاه می شود ، بنابراین بسیار مهم است که به ترتیب صحیح باشد ، و این چیزی نیست که از آن استفاده نکند!

این بدان معناست که MotableVec نیاز به حفظ صف تغییرات دارد ، بنابراین این هزینه عملکرد جزئی دارد.

اما از آنجا که بر اساس تفاوت بین VEC قدیمی و جدید ، به روزرسانی بسیار کارآمد است ، هنوز هم استفاده از motablevec به جای اینکه قابل تغییر باشد ، حتی با عملکرد اضافی نیز سریعتر است.

علاوه بر این ، حتی اگر MotableVec نیاز به حفظ صف داشته باشد ، SignalVec چنین نمی کند ، بنابراین کاملاً کارآمد است.

اگر به یک روش motableVec می گویید که در واقع هیچ تغییری ایجاد نمی کند ، به هیچ وجه به آن اطلاع نمی دهد:

روش نگهدارنده MutableVEC :: همان VEC است :: حفظ ، بسته شدن هر مقدار را در MutableVEC می نامد ، و اگر بسته شدن کاذب را برگرداند ، آن مقدار را از MUTABLEVEC حذف می کند.

اما در مثال بالا ، هرگز نادرست برنمی گردد ، بنابراین هرگز چیزی را از بین نمی برد ، بنابراین به آن اطلاع نمی دهد.

همچنین ، حتی اگر ارسال اعلان برای هر تغییر تضمین شده باشد ، ممکن است اعلان متفاوت از آنچه انتظار دارید متفاوت باشد.

به عنوان مثال ، هنگام فراخوانی روش نگهدارنده ، برای هر تغییر یک اعلان ارسال می کند ، بنابراین اگر حفظ 5 مقدار را حذف کند ، 5 اعلان را ارسال می کند.

اما ، برخلاف آنچه ممکن است انتظار داشته باشید ، اعلان ها به ترتیب معکوس هستند: ابتدا اعلان هایی را برای مقادیر راست اول ارسال می کند ، و اعلان هایی برای ارزش های سمت چپ ماندگار است. علاوه بر این ، ترکیبی از Vecdiff :: Pop and Vecdiff :: حذف را ارسال می کند.

مثال دیگر این است که حذف ممکن است با Vecdiff :: حذف یا Vecdiff :: POP بسته به اینکه آخرین مقدار را حذف کرده باشد یا خیر.

دلیل این امر عملکرد است و شما نباید به آن اعتماد کنید: رفتار دقیقاً که اعلان ها ارسال می شوند ، جزئیات اجرای آن است.

با این حال ، یک چیز وجود دارد که می توانید به آن اعتماد کنید: اگر اعلان ها را به همان ترتیب دریافت کنید ، دقیقاً SignalVec را بازآفرینی می کند:

در مثال بالا ، COPIED_VEC تضمین می شود که دقیقاً همان مقادیر MY_VEC را به همان ترتیب My_VEC داشته باشد.

اما حتی اگر نتیجه نهایی یکسان باشد ، ترتیب تغییرات فردی یک جزئیات اجرای نامشخص است.

stereemap

دقیقاً مانند اینکه MotableVec نسخه سیگنال VEC است ، همچنین MutableBtreemap وجود دارد که یک نسخه سیگنال BTreemap است:

مشابه با MUTABLEVEC ، آن را با MapDiff اطلاع می دهد ، و البته برای تحولات و اعلان های کارآمد از نقشه سیگنال و پیام سیگنال پشتیبانی می کند:

و این پایان آموزش است! ما هر روش را پوشش ندادیم ، اما به اندازه کافی برای شروع کار شما را پوشانده ایم.

شما می توانید برای اطلاعات در مورد هر روش به اسناد و مدارک نگاه کنید (چیزهای مفیدی در آنجا وجود دارد!).

فارکس وکسب درامد...
ما را در سایت فارکس وکسب درامد دنبال می کنید

برچسب : نویسنده : آرش اصل زاد بازدید : <-PostHit-> تاريخ : سه شنبه 30 خرداد 1402 ساعت: 12:43