لماذا يكون الموازي . حيث لا تعمل عند التحويل إلى الملاحظة؟

StackOverflow https://stackoverflow.com/questions/2287963

سؤال

لديّ مجموعة يمكن ملاحظتها أرغب في معالجتها بالتوازي ، ثم لاحظ القيم المعالجة أثناء التصفية واشتراك معالج أخيرًا يتلقى القيم التي تمت تصفيتها.

نموذجي صحي 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 على سبيل المثال ، وأظن أنك سترى نفس النتيجة.

لا أعرف لماذا لا يعمل في الحالة الموازية على الرغم من ... هل يمكنني أن أقترح أن تضع بعض التسجيل لمعرفة مدى وصول البيانات؟ يمكنك تنفيذ تسجيل مفيد مع تحديد والذي يقوم بإرجاع العنصر الأصلي بعد تسجيله ، على سبيل المثال.

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