سؤال

متى سيختار المرء استخدام RX عبر TPL أم أن الأطر 2 متعامدة؟

من ما أفهمه ، يهدف RX بشكل أساسي إلى توفير تجريد على الأحداث والسماح بالتكوين ولكنه يسمح أيضًا بتوفير تجريد على عمليات التزامن. باستخدام الحمولة الزائدة Createxx و Aromxxx Overloads والإلغاء عن طريق التخلص من idisposable التي تم إرجاعها.

يوفر TPL أيضًا تجريدًا للعمليات من خلال قدرات المهمة والإلغاء.

معضاتي هي متى يجب استخدام أي سيناريوهات؟

هل كانت مفيدة؟

المحلول

الغرض الرئيسي من RX هو عدم توفير تجريد على الأحداث. هذه مجرد واحدة من نتائجها. الغرض الأساسي هو توفير نموذج دفع قابل للتأليف للمجموعات.

يعتمد الإطار التفاعلي (RX) على IObservable<T> كونه المزدوج الرياضي IEnumerable<T>. لذا بدلاً من "سحب" العناصر من مجموعة باستخدام IEnumerable<T> يمكن أن يكون لدينا أشياء "دفعت" لنا عبر IObservable<T>.

بالطبع ، عندما نذهب فعليًا للبحث عن مصادر يمكن ملاحظتها ، فإن أشياء مثل الأحداث وعمليات Async هي مرشحين ممتازين.

يتطلب الإطار التفاعلي بشكل طبيعي أن يكون نموذج متعدد الخيوط قادرًا على مشاهدة مصادر البيانات التي يمكن ملاحظتها وإدارة الاستعلامات والاشتراكات. RX في الواقع يستخدم بكثافة TPL للقيام بذلك.

لذلك إذا كنت تستخدم RX ، فأنت تستخدم ضمنيًا TPL.

يمكنك استخدام TPL مباشرة إذا كنت ترغب في التحكم المباشر في مهامك.

ولكن إذا كان لديك مصادر للبيانات التي ترغب في مراعاتها وتنفيذها مقابلات ، فإنني أوصي تمامًا بالإطار التفاعلي.

نصائح أخرى

بعض الإرشادات التي أحب متابعتها:

  • هل أتعامل مع البيانات التي لا نشأت. البيانات التي تصل عندما ترضي؟ ثم RX.
  • هل نشأت حسابات وأحتاج إلى إدارة التزامن؟ ثم TPL.
  • هل أدير نتائج متعددة ، وأحتاج إلى الاختيار منها بناءً على الوقت؟ ثم RX.

أنا أحب نقاط رصاصة سكوت دبليو. لوضع بعض الأمثلة الملموسة في خرائط RX بشكل جيد حقًا

  • استهلاك التدفقات
  • أداء عمل غير متزامن غير محظور مثل طلبات الويب.
  • أحداث البث (إما أحداث .NET مثل حركة الماوس أو أحداث نوع رسالة حافلة الخدمة)
  • تأليف "تدفقات" الأحداث معًا
  • عمليات نمط LINQ
  • تعريض تدفقات البيانات من واجهة برمجة التطبيقات العامة الخاصة بك

يبدو أن TPL يخطط بشكل جيد

  • التوازي الداخلي للعمل
  • أداء عمل غير متزامن غير محظور مثل طلبات الويب
  • أداء تدفقات العمل والاستمرارية

شيء واحد لاحظته مع iobservable (RX) هو أنه يصبح منتشرة. بمجرد وصوله إلى قاعدة التعليمات البرمجية ، حيث سيتم تعريضها بلا شك عبر واجهات أخرى ، فسيظهر في النهاية في جميع أنحاء التطبيق الخاص بك. أتصور أن هذا قد يكون مخيفًا في البداية ، لكن معظم الفريق مرتاح للغاية مع RX الآن ويحب مقدار العمل الذي يوفره لنا.

ستكون IMHO RX هي المكتبة المهيمنة على TPL لأنها مدعومة بالفعل في .NET 3.5 و 4.0 و Silverlight 3 و Silverlight 4 و JavaScript. هذا يعني أنه يتعين عليك تعلم نمط واحد بشكل فعال وهو ينطبق على العديد من المنصات.

تعديل: لقد غيرت رأيي حول كون RX مهيمنًا على TPL. إنهم يحلون مشاكل مختلفة ، لذا لا ينبغي مقارنتها حقًا على هذا النحو. مع .NET 4.5/C# 5.0 ، فإن الكلمات الرئيسية ASYNC/AWAIT ستعمل على زيادة ربطنا بـ TPL (وهو أمر جيد). للحصول على قرص عميق على RX vs Events مقابل TPL إلخ. تحقق من الفصل الأول من كتابي على الإنترنت introtorx.com

تحديث ، ديسمبر 2016: إذا كان لديك 30 دقيقة ، فإنني أوصيك بقراءة حساب Joe Duffy المباشر بدلاً من المضاربة. أعتقد أن تحليلي يصمد بشكل جيد ، ولكن إذا وجدت هذا السؤال ، فإنني أوصي بشدة أن ترى منشور المدونة بدلاً من هذه الإجابات لأنه بالإضافة إلى TPL vs Rx.net ، يغطي أيضًا مشاريع البحث MS (Midori ، Cosmos).

http://joeduffyblog.com/2016/11/30/15 سنة


أعتقد أن MS ارتكبت خطأً كبيراً في الإفراط في التصحيح بعد خرج .NET 2.0. لقد قدموا العديد من واجهات برمجة التطبيقات المختلفة لإدارة التزامن في نفس الوقت من أجزاء مختلفة من الشركة.

  • كان ستيفن توب يضغط بشدة على بدائل الآمنة من الخيوط لتحل محل الحدث (الذي بدأ كـ Future<T> وتحولت إلى Task<T>)
  • كان MS Research min-linq والامتدادات التفاعلية (RX)
  • كان الأجهزة/المضمنة الروبوتات العضلية (CCR)

في غضون ذلك ، كانت العديد من فرق واجهة برمجة التطبيقات التي تمكنت منها تحاول العيش مع APM و Threadpool.QueueUserWorkItem(), ، عدم معرفة ما إذا كان Toub سيفوز معركته للشحن Future<T>/Task<T> في mscorlib.dll. في النهاية ، يبدو أنهم يتحولون ، وشحنهما على حد سواء Task<T> و IObservable<T> في mscorlib ، ولكن لم يسمح لأي واجهات برمجة التطبيقات الأخرى RX (ولا حتى ISubject<T>) في mscorlib. أعتقد أن هذا التحوط انتهى به الأمر مما تسبب في قدر كبير من الازدواجية (أكثر لاحقًا) وتهدر الجهد داخل الشركة وخارجها.

للاشتعال الازدواجية: Task ضد. IObservable<Unit>, Task<T> ضد. AsyncSubject<T>, Task.Run() ضد. Observable.Start(). وهذا هو مجرد غيض من الجبل الجليدي. ولكن على مستوى أعلى النظر في:

  • تدفقات حدث STEMANINSIGH
  • TPL Dataflow - مبني على TPL ، المدمج بالتوازي مع RX ، محسّن للتبديل في ترابط الخيوط ، وليس جيدًا في تأليف الاستعلامات
  • RX - تعبير مذهل ، ولكن محفوفة بالمخاطر. يمزج الجداول "الساخنة" مع IEnumerable-طرق التمديد ، مما يعني أنك تمنع بسهولة إلى الأبد (الاتصال First() على تيار ساخن لا يعود أبدًا). تتم حدود الجدولة (الحد من التوازي) عبر غريب إلى حد ما SubscribeOn() طرق التمديد ، والتي تكون ضمنية بشكل غريب ويصعب الحصول عليها. إذا بدأت في تعلم RX حجز وقتًا طويلاً لتعلم جميع المزالق لتجنبها. لكن RX هو حقًا الخيار الوحيد إذا قامت بتأليف تدفقات الأحداث المعقدة أو كنت بحاجة إلى تصفية/استعلام معقدة.

لا أعتقد أن RX لديه فرصة قتال في اعتماد واسع حتى سفن MS ISubject<T> في mscorlib. وهو أمر محزن ، لأن RX يحتوي على بعض أنواع الخرسانة المفيدة للغاية (العامة) ، مثل TimeInterval<T> و Timestamped<T>, ، الذي أعتقد أنه ينبغي أن يكون في Core/Mscorlib مثل Nullable<T>. ايضا، System.Reactive.EventPattern<TEventArgs>.

أود أن أقول أن TPL Dataflow يغطي مجموعة فرعية متخصصة من الوظائف في RX. Dataflow مخصص لمعالجة البيانات التي يمكن أن تستغرق وقتًا قابلة للقياس ، في حين أن RX مخصص للأحداث ، مثل موضع الماوس ، وحالات الخطأ ، وما إلى ذلك حيث يكون الوقت ضئيلًا.

مثال: معالج "الاشتراك" الخاص بك غير متزامن ولا تريد أكثر من 1 منفذ في ذلك الوقت. مع RX ، يجب عليك منعه ، لا توجد طريقة أخرى للتغلب عليها ، لأن RX غير متزامن لا يتزامن ولا يهدد غير متزامن بطريقة خاصة في العديد من الأماكن.

.Subscribe(myAsyncHandler().Result)

إذا لم تقم بمنعك ، فسوف تنظر RX في أن الإجراء قد اكتمل بينما لا يزال يتم تنفيذ المعالج بشكل غير متزامن.

قد تعتقد أنه إذا قمت بذلك

.ObserveOn(Scheduler.EventLoopSchedule)

تم حل المشكلة. ولكن هذا سيؤدي إلى كسر سير العمل .complete () ، لأن RX ستعتقد أنه يتم بمجرد جدولة التنفيذ وستتوقف عن تطبيقك دون انتظار إكمال تشغيل Async.

إذا كنت ترغب في السماح بأكثر من 4 مهام غير متزامنة متزامنة من RX ، فلا تقدم أي شيء خارج الصندوق. ربما يمكنك اختراق شيء ما عن طريق تنفيذ جدولك الخاص ، المخزن المؤقت ، إلخ.

تقدم TPL DataFlow حلًا رائعًا جدًا في ActionBlock. يمكن أن تخنق الإجراءات المتزامنة إلى عدد معين ، وهو يفهم العمليات ASYNC ، لذا فإن استدعاء COMMUNT () والانتظار عن الانتهاء سيفعل ما تتوقعه بالضبط: في انتظار إكمال جميع مهام ASYNC الموجودة.

ميزة أخرى TPL لديها "ضغط الخلفية". دعنا نقول أنك اكتشفت خطأ في روتين التعامل الخاص بك وتحتاج إلى إعادة حساب بيانات الشهر الماضي. إذا قمت بالاشتراك في مصدرك باستخدام RX ، وكان خط الأنابيب الخاص بك يحتوي على مخازن مؤقتة غير محدودة ، أو مراقبة ، مما ستنفد الذاكرة في غضون ثوانٍ لأن المصدر سيستمر في القراءة بشكل أسرع مما يمكن للمعالجة معالجته. حتى إذا قمت بتطبيق مستهلك حظر ، فقد يعاني مصدرك من منع المكالمات ، على سبيل المثال إذا كان المصدر غير متزامن. في TPL يمكنك تنفيذ المصدر كـ

while(...)
    await actionBlock.SendAsync(msg)

الذي لا يمنع المصدر حتى الآن سوف ينتظر بينما يتم تحميل المعالج.

بشكل عام ، وجدت أن RX مناسب للإجراءات التي هي الوقت والضوء الحسابي. إذا أصبح وقت المعالجة كبيرًا ، فأنت في عالم الآثار الجانبية الغريبة وتصحيح الأخطاء الباطنية.

الأخبار السارة هي أن كتل TPL DataFlow تلعب لطيفة جدًا مع RX. لديهم محولات asobserver/asobservable ويمكنك التمسك بها في منتصف خط أنابيب RX عند الحاجة. لكن RX لديها المزيد من الأنماط والحالات الاستخدام. لذا فإن قاعدة الإبهام الخاصة بي هي البدء بـ RX وإضافة TPL Dataflow حسب الحاجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top