أفضل الممارسات في تصميم مؤشرات الترابط المتعددة

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

  •  08-06-2019
  •  | 
  •  

سؤال

النظر في هذه المشكلة:لدي برنامج يجب أن يجلب (على سبيل المثال) 100 سجل من قاعدة بيانات، وبعد ذلك يجب أن يحصل على معلومات محدثة لكل سجل من خدمة الويب.هناك طريقتان لتقديم التوازي في هذا السيناريو:

  1. أبدأ كل طلب إلى خدمة الويب في موضوع جديد.يتم التحكم في عدد الخيوط المتزامنة بواسطة بعض المعلمات الخارجية (أو يتم تعديلها ديناميكيًا بطريقة ما).

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

ما هو النهج الأفضل، ولماذا تعتقد ذلك؟

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

المحلول

الخيار 3 هو الأفضل:

استخدم الإدخال/الإخراج غير المتزامن.

ما لم تكن معالجة طلبك معقدة وثقيلة، فسيقضي برنامجك 99% من وقته في انتظار طلبات HTTP.

هذا هو بالضبط ما تم تصميم Async IO من أجله - دع حزمة شبكات Windows (أو إطار عمل .net أو أي شيء آخر) تقلق بشأن كل الانتظار، واستخدم مؤشر ترابط واحد فقط لإرسال النتائج و"التقاطها".

لسوء الحظ، فإن إطار عمل .NET يجعل الأمر مؤلمًا في المؤخرة.يكون الأمر أسهل إذا كنت تستخدم مآخذ توصيل خام فقط أو Win32 api.إليك مثال (تم اختباره!) باستخدام C#3 على أي حال:

using System.Net; // need this somewhere

// need to declare an class so we can cast our state object back out
class RequestState {
    public WebRequest Request { get; set; }
}

static void Main( string[] args ) {
    // stupid cast neccessary to create the request
    HttpWebRequest request = WebRequest.Create( "http://www.stackoverflow.com" ) as HttpWebRequest;

    request.BeginGetResponse(
        /* callback to be invoked when finished */
        (asyncResult) => { 
            // fetch the request object out of the AsyncState
            var state = (RequestState)asyncResult.AsyncState; 
            var webResponse = state.Request.EndGetResponse( asyncResult ) as HttpWebResponse;

            // there we go;
            Debug.Assert( webResponse.StatusCode == HttpStatusCode.OK ); 

            Console.WriteLine( "Got Response from server:" + webResponse.Server );
        },
        /* pass the request through to our callback */
        new RequestState { Request = request }  
    );

    // blah
    Console.WriteLine( "Waiting for response. Press a key to quit" );
    Console.ReadKey();
}

يحرر:

في حالة .NET، يتم إطلاق "رد اتصال الإكمال" فعليًا في سلسلة رسائل ThreadPool، وليس في سلسلة المحادثات الرئيسية، لذلك ستظل بحاجة إلى قفل أي موارد مشتركة، لكنه لا يزال يوفر عليك كل عناء إدارة سلاسل الرسائل.

نصائح أخرى

شيئان يجب مراعاتهما.

1.كم من الوقت ستستغرق معالجة السجل؟

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

إذا كانت معالجة السجلات مستمرة لفترة طويلة إلى حد معقول، فسيكون الفرق ضئيلًا، لذا من المحتمل أن يكون الأسلوب الأبسط (سجل واحد لكل سلسلة) هو الأفضل.

2.كم عدد المواضيع التي تخطط لبدءها؟

إذا كنت لا تستخدم مجموعة مؤشرات الترابط، فأعتقد أنك تحتاج إما إلى تحديد عدد سلاسل الرسائل يدويًا، أو تحتاج إلى تقسيم البيانات إلى أجزاء كبيرة.سيؤدي بدء سلسلة محادثات جديدة لكل سجل إلى تعطل نظامك إذا زاد عدد السجلات.

ربما لا يكون الكمبيوتر الذي يقوم بتشغيل البرنامج هو عنق الزجاجة، لذلك:تذكر أن بروتوكول HTTP يحتوي على رأسية للمحافظة على الحياة، تتيح لك إرسال عدة طلبات GET على نفس المقابس، مما يوفر عليك من اهتزاز TCP/IP.لسوء الحظ، لا أعرف كيفية استخدام ذلك في مكتبات .net.(يجب أن يكون ممكنا.)

ومن المحتمل أيضًا أن يكون هناك تأخير في الرد على طلباتك.يمكنك محاولة التأكد من أن لديك دائمًا عددًا معينًا من الطلبات المعلقة للخادم.

احصل على ال الفوركس الموازي.انظر إلى BlockingCollection.استخدم خيطًا لإطعامه دفعات من السجلات، وسلاسل من 1 إلى n لسحب السجلات من المجموعة إلى الخدمة.يمكنك التحكم في معدل تغذية المجموعة وعدد سلاسل العمليات التي تتصل بخدمات الويب.اجعله قابلاً للتكوين عبر ConfigSection، واجعله عامًا عن طريق تغذية مندوبي الإجراء للمجموعة، وسيكون لديك أداة تجميع صغيرة لطيفة يمكنك إعادة استخدامها بما يرضيك.

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