سؤال

لدي حلقة متوازية. قم بإدارة عملية مكثفة داخل الجسم.

يمكن للعملية استخدام hashtable لتخزين القيم، ويمكن إعادة استخدامها لعناصر حلقة أخرى أخرى. أضيف إلى القابل للصداء بعد عملية التشغيل المكثفة كاملة، يمكن أن يبحث عنصر الحلقة التالية البحث عن الكائن القابل لإعادة استخدامه وإعادة استخدامه، بدلا من تشغيل العملية المكثفة مرة أخرى.

ومع ذلك، نظرا لأنني أستخدم متوازي. فهناك مشكلة غير آمنة، تسبب مكالمات Hashtable.add و Syneskey (المفتاح) تخرج من المزامنة، لأنها قد تعمل بالتوازي. إدخال الأقفال قد يسبب مشاكل PERF.

إليك نموذج التعليمات البرمجية:

Hashtable myTable = new Hashtable;
Parallel.ForEach(items, (item, loopState) =>
{
    // If exists in myTable use it, else add to hashtable
    if(myTable.ContainsKey(item.Key))
    {
       myObj = myTable[item.Key];
    }
    else
    {
       myObj = SomeIntensiveOperation();
       myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime
    }
    // Do something with myObj
    // some code here
}

يجب أن يكون هناك بعض API، إعداد الممتلكات داخل مكتبة TPL، والتي يمكن أن تتعامل مع هذا السيناريو. هل هناك؟

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

المحلول

أنت تبحث عن System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>. وبعد تستخدم المجموعات المتزامنة الجديدة آليات قفل محسنة بشكل ملحوظ، وينبغي أن تؤدي expectely في الخوارزميات الموازية.

تحرير: قد تبدو النتيجة مثل هذا:

ConcurrentDictionary<T,K> cache = ...;
Parallel.ForEach(items, (item, loopState) =>
{
    K value;
    if (!cache.TryGetValue(item.Key, out value))
    {
        value = SomeIntensiveOperation();
        cache.TryAdd(item.Key, value);
    }

    // Do something with value
} );

كلمة تحذير: إذا كانت العناصر في items ليس كلها فريدة من نوعها item.Key, ، ومن بعد SomeIntensiveOperation يمكن أن تسمى مرتين لهذا المفتاح. في المثال، لا يتم تمرير المفتاح إلى SomeIntensiveOperation, ، وهذا يعني أن رمز "القيام بشيء ما مع القيمة" يمكنه تنفيذ أزواج المفتاح / القيمة والمفتاح / القيمة الرئيسية، وسوف يتم تخزين نتيجة واحدة فقط في ذاكرة التخزين المؤقت (وليس بالضرورة أول واحد محسوب عن طريق التضارب أيضا). كنت بحاجة إلى مصنع كسول مواز للتعامل مع هذا إذا انها مشكلة. أيضا، لأسباب واضحة يجب أن تكون بعض الشيء آمنة للخيط.

نصائح أخرى

افحص ال system.collections.concurrent. مساحة الاسم أعتقد أنك بحاجة oncurrentdiction.

استخدم ReaderWhiterLock، وهذا لديه أداء جيد للعمل الذي لديه العديد من القراءات وعدد قليل يكتب من مدة قصيرة. يبدو أن مشكلتك تناسب هذه المواصفات.

سيتم تشغيل جميع عمليات القراءة بسرعة وقفل الحرة، والوقت الوحيد الذي سيتم حظره هو عندما يحدث الكتابة، وأن الكتابة هي فقط طالما أن تستغرق دفع شيء ما في القابل له.

ReaderWireiterLocksLim على MSDN.

أعتقد أنني سوف ألقي بعض الكود ...

ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
Hashtable myTable = new Hashtable();
Parallel.ForEach(items, (item, loopState) =>
{
    cacheLock.EnterReadLock();
    MyObject myObj = myTable.TryGet(item.Key);
    cacheLock.ExitReadLock();

    // If the object isn't cached, calculate it and cache it
    if(myObj == null)
    {
       myObj = SomeIntensiveOperation();
       cacheLock.EnterWriteLock();
       try
       {
           myTable.Add(item.Key, myObj);
       }
       finally
       {
           cacheLock.ExitWriteLock();
       }           
    }
    // Do something with myObj
    // some code here
}

static object TryGet(this Hashtable table, object key)
{
    if(table.Contains(key))
        return table[key]
    else
        return null;
}

لا أرى أي خيار آخر صحيح بدلا من استخدام أقفال (أكثر أو أقل صريحة) (تتجاوز القابلة للتجزئة المتزامنة فقط جميع الأساليب بأقفال).

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

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