Multithreading: كيفية معالجة البيانات في ناقل ، بينما يتم ملء المتجه؟
-
02-10-2019 - |
سؤال
لديّ تطبيق Linux ذو الخيال الواحد الذي أود أن أجعله بالتوازي. يقرأ ملف بيانات ، ويقوم بإنشاء كائنات ، ويضعها في متجه. ثم يستدعي طريقة كثيفة الحساب (.5 ثانية+) على كل كائن. أريد أن أتصل بالطريقة بالتوازي مع إنشاء الكائن. بينما نظرت إلى QT و TBB ، أنا منفتح على خيارات أخرى.
خططت لبدء الخيط (الخيط) بينما كان المتجه فارغًا. كل واحد يمكن أن يتصل به makeSolids
(أدناه) ، التي لديها حلقة من الوقت والتي سيتم تشغيلها حتى يتم معالجة جميع الكائنات في المتجه. ومع ذلك ، فأنا N00B عندما يتعلق الأمر بالخيوط ، وكنت أبحث عن حل جاهز.
QtConcurrent::map(Iter begin,Iter end,function())
تبدو سهلة للغاية ، لكن لا يمكنني استخدامه على متجه يتغير في الحجم ، هل يمكنني ذلك؟ وكيف أخبرها أن تنتظر المزيد من البيانات؟
نظرت أيضًا إلى TBB من Intel ، لكن يبدو أن موضوعي الرئيسي سيتوقف إذا استخدمت parallel_for
أو parallel_while
. هذا ينتن ، منذ أن أوصى مدير الذاكرة الخاص بهم (Open MMGT's MMGT لديه أداء ضعيف عند تعدد التربعات من العمر).
/**intended to be called by a thread
\param start the first item to get from the vector
\param skip how many to skip over (4 for 4 threads)
*/
void g2m::makeSolids(uint start, uint incr) {
uint curr = start;
while ((!interpDone) || (lineVector.size() > curr)) {
if (lineVector.size() > curr) {
if (lineVector[curr]->isMotion()) {
((canonMotion*)lineVector[curr])->setSolidMode(SWEPT);
((canonMotion*)lineVector[curr])->computeSolid();
}
lineVector[curr]->setDispMode(BEST);
lineVector[curr]->display();
curr += incr;
} else {
uio::sleep(); //wait a little bit for interp
}
}
}
تحرير: لتلخيص ، ما هي أبسط طريقة لمعالجة المتجه في نفس الوقت الذي يقوم فيه الخيط الرئيسي بملء المتجه؟
المحلول
من الصعب معرفة ما إذا كنت تفكر في هذه المشكلة بعمق وهناك أكثر مما تتركه ، أو إذا كنت أكثر من التفكير في ذلك ، أو إذا كنت حذرًا من الخيط.
قراءة الملف وإنشاء الكائنات سريعة ؛ الطريقة الوحيدة بطيئة. التبعية هي أن كل CTOR متتالية يعتمد على نتائج CTOR السابقة - غريب بعض الشيء - ولكن على خلاف ذلك ، لا توجد مشكلات في تكامل البيانات ، لذلك لا يبدو أن هناك أي شيء يحتاج إلى حماية بواسطة Mutexes وما إلى ذلك.
لماذا هذا أكثر تعقيدًا من شيء من هذا القبيل (في الكود الكاذب الخام):
while (! eof)
{
readfile;
object O(data);
push_back(O);
pthread_create(...., O, makeSolid);
}
while(x < vector.size())
{
pthread_join();
x++;
}
إذا كنت لا ترغب في حلقة على الوصلات في الرئيسية الخاصة بك ، فقم بتفريغ خيط لانتظاره عن طريق تمرير متجه من TIDs.
إذا كان عدد الكائنات/مؤشرات الترابط التي تم إنشاؤها مجنونة ، فاستخدم تجمع مؤشرات الترابط. أو وضع العداد هو حلقة الإنشاء للحد من عدد المواضيع التي يمكن إنشاؤها قبل الانضمام إلى الجري.
نصائح أخرى
أولاً ، للاستفادة من الخيوط ، تحتاج إلى العثور على مهام بطيئة بالمثل لكل موضوع للقيام به. قلت أن المعالجة لكل كائن تستغرق .5S+، ما هي المدة التي تستغرقها قراءة الملف / إنشاء الكائن؟ يمكن أن يكون بسهولة عُشر أو ألف من ذلك الوقت ، وفي هذه الحالة ، سيؤدي نهجك المتعدد القراءة إلى تحقيق فائدة سلبية. إذا كان هذا هو الحال ، (نعم ، سأجيب على سؤالك الأصلي قريبًا في حالة عدم وجوده) ، ففكر في معالجة كائنات متعددة في وقت واحد. نظرًا للمعالجات الخاصة بك تستغرق بعض الوقت ، فإن إنشاء مؤشرات الترابط النفقات العامة ليست ذات أهمية فظيعة ، لذلك يمكنك ببساطة أن تولد مؤشر ترابط قراءة الملف الرئيسي/إنشاء الكائن مؤشر ترابط جديد وتوجيهه إلى الكائن الذي تم إنشاؤه حديثًا. ثم يواصل الخيط الرئيسي قراءة/إنشاء كائنات لاحقة. بمجرد قراءة/إنشاء جميع الكائنات ، وجميع مؤشرات ترابط المعالجة التي تم إطلاقها ، فإن الخيط الرئيسي "ينضم" (ينتظر) مؤشرات ترابط العمال. إذا كان هذا سيؤدي إلى إنشاء العديد من المواضيع (الآلاف) ، فضع حدًا على مدى مسموح لخيط الخيط الرئيسي بالوصول إلى: قد يقرأ/إنشاء 10 كائنات ثم انضم إلى 5 ، ثم قراءة/إنشاء 10 ، انضم إلى 10 ، قراءة/إنشاء 10 ، انضم 10 إلخ حتى الانتهاء.
الآن ، إذا كنت تريد حقًا أن تكون القراءة/إنشاء بالتوازي مع المعالجة ، ولكن يتم التسلسل للمعالجة ، فلا يزال بإمكانك استخدام النهج أعلاه ولكن الانضمام بعد كل كائن. هذا نوع من الغريب إذا كنت تقوم بتصميم هذا مع وضع هذا النهج فقط ، ولكنه جيد لأنه يمكنك بسهولة تجربة معالجة الكائنات الموازية أعلاه أيضًا.
بدلاً من ذلك ، يمكنك استخدام نهج أكثر تعقيدًا يتضمن فقط الخيط الرئيسي (الذي ينشئه نظام التشغيل عند بدء تشغيل البرنامج) ، وخيط عامل واحد يجب أن يبدأه الخيط الرئيسي. يجب تنسيقها باستخدام Mutex (متغير ضمان الحصص المتبادلة ، مما يعني عدم تواجدها ، والوصول إلى البيانات) ، ومتغير شرط يسمح لخيط العامل بحظر كفاءة حتى يوفر مؤشر الترابط الرئيسي المزيد من العمل. المصطلحات - متغير Mutex والشرط - هي المصطلحات القياسية في سلسلة خيوط Posix التي يستخدمها Linux ، لذلك يجب استخدامها في شرح المكتبات المعينة التي تهتم بها. بشكل ملخص ، ينتظر مؤشر ترابط العامل حتى القراءة الرئيسية/إنشاء مؤشر ترابط يبثها إشارة إيقاظ تشير إلى أن كائن آخر جاهز للمعالجة. قد ترغب في الحصول على عداد مع فهرس آخر كائن تم إنشاؤه بالكامل ، جاهز للمعالجة ، بحيث يمكن لخيط العامل الحفاظ على عدد الكائنات المصنعة والتحرك على طول الكائنات الجاهزة قبل التحقق مرة أخرى من متغير الحالة.
Caleb: تمامًا - ربما كان ينبغي علي التأكيد نشيط الخيوط. يجب دائمًا اعتبار موضوع واجهة المستخدم الرسومية واحدًا.