لماذا يكون الموازي . حيث لا تعمل عند التحويل إلى الملاحظة؟
-
21-09-2019 - |
سؤال
لديّ مجموعة يمكن ملاحظتها أرغب في معالجتها بالتوازي ، ثم لاحظ القيم المعالجة أثناء التصفية واشتراك معالج أخيرًا يتلقى القيم التي تمت تصفيتها.
نموذجي صحي Where
بيان القيام بالتصفية يتم تقييمه. ولكن لا توجد بيانات تأتي من خلال الاشتراك. إذا أزلت AsParallel
بحيث تتم المعالجة على أساس منتظم IEnumerable
, ، تأتي البيانات ويعمل كل شيء كما هو متوقع.
إليكم عينة ، القيام ببعض المعالجة على الأوتار:
// Generate some data every second
var strings = Observable.Generate(() =>
new TimeInterval<Notification<string>>(
new Notification<string>
.OnNext(DateTime.Now.ToString()), TimeSpan.FromSeconds(1)));
// Process the data in parallel
var parallelStrings = from value in strings.ToEnumerable().AsParallel()
select "Parallel " + value;
// Filter and observe
var data = String.Empty;
parallelStrings
.Where(value => !String.IsNullOrEmpty(value))
.ToObservable()
.Subscribe(value => data = value);
الشيء الغريب التالي هو أنه إذا استخدمت TakeWhile
المشغل ، الذي يشبه في رأيي من الناحية النظرية حيث تعمل مراقبة الموازية كما هو متوقع:
// Filter and observe
var data = String.Empty;
parallelStrings
.TakeWhile(cs => !String.IsNullOrEmpty(cs))
.ToObservable()
.Subscribe(value => data = value);
تُظهر إضافة بعض رمز التسجيل إلى الاشتراك أنه تم استلام البيانات حتى ToObservable
التحويل ، ولكن ليس بعد:
1. var data = String.Empty;
2. parallelStrings
3. .Where(value => !String.IsNullOrEmpty(value))
4. .Select(value => value)
5. .ToObservable()
6. .Select(value => value)
7. .Subscribe(value => data = value);
يتم ضرب نقطة توقف في Lambda في السطر 4 بينما لم يتم ضرب نقطة توقف في Lambda في السطر 6.
لماذا سوف TakeWhile
جعل البيانات تأتي من خلال المشترك أثناء Where
لا؟
إذا كان الأمر ذا أهمية ، فأنا أقوم بتطوير الكود في Visual Studio 2010 RC مع ملف تعريف عميل Framework .NET 4.0.
تحديث: مرتكز على sergeys الإجابة أعدت صياغة وضع Where
منقي. الرمز التالي يعمل كما هو متوقع:
var processedStrings = from value in strings
let processedValue = "Parallel " + value
where !String.IsNullOrEmpty(processedValue)
select processedValue;
var data = String.Empty;
processedStrings
.ToEnumerable()
.AsParallel()
.ToObservable()
.Subscribe(value => data = value );
لا يزال من المحرج بعض الشيء أن تضطر أولاً إلى تحويل الأولي القابل للملاحظة processedStrings
في التعداد من أجل موازاةه ، ثم تحويله مرة أخرى إلى مراقبة من أجل الاشتراك في النتيجة النهائية.
المحلول
من C# 4.0 باختصار:
هناك حاليًا بعض القيود العملية على ما يمكن أن يتوازى Plinq. قد تخفف هذه القيود من خلال حزم الخدمة اللاحقة والإصدارات الإطارية. يمنع مشغلو الاستعلام التاليون استعلامًا من التوازي ، ما لم تكن عناصر المصدر في موضع الفهرسة الأصلي:
- خذ ، تتبع ، تخطي ، وتخطي
- الإصدارات المفهرسة من Select و Selectany و Elementat
يغير معظم مشغلي الاستعلام موضع الفهرسة للعناصر (بما في ذلك تلك التي تزيل العناصر ، مثل أين). هذا يعني أنه إذا كنت ترغب في استخدام المشغلين السابقون ، فسوف يحتاجون عادة إلى أن يكونوا في بداية الاستعلام
لذلك ، في الواقع ، فإن استخدام Takehile يمنع .assalalal () من التوازي. من الصعب قول هذا لماذا حيث يقتل العاطفة ، ولكن وضعه قبل الهزيمو ربما إصلاح المشكلة.
نصائح أخرى
TakeWhile
ليس من الناحية المفاهيمية Where
, ، لأنه يعتمد على الطلب. أظن أن الاستعلام فعلا التنفيذ بالتتابع (انظر منشور المدونة هذا). حاول الاتصال .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
في الخاص بك TakeWhile
على سبيل المثال ، وأظن أنك سترى نفس النتيجة.
لا أعرف لماذا لا يعمل في الحالة الموازية على الرغم من ... هل يمكنني أن أقترح أن تضع بعض التسجيل لمعرفة مدى وصول البيانات؟ يمكنك تنفيذ تسجيل مفيد مع تحديد والذي يقوم بإرجاع العنصر الأصلي بعد تسجيله ، على سبيل المثال.