كيف يمكن تحسين هذا الأداء ، الفظيعة المنفذ التسلسلي الرمز ؟

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

سؤال

لدي قطعة القبيح من المنفذ التسلسلي رمز غير مستقر للغاية.

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(100);
    while (port.BytesToRead > 0)
    {
        var count = port.BytesToRead;

        byte[] buffer = new byte[count];

        var read = port.Read(buffer, 0, count);

        if (DataEncapsulator != null)
            buffer = DataEncapsulator.UnWrap(buffer);


       var response = dataCollector.Collect(buffer);

       if (response != null)
       {
           this.OnDataReceived(response);
       }

       Thread.Sleep(100);
    }    
}

إذا قمت بإزالة أي موضوع.النوم(100) إلى رمز يتوقف عن العمل.

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

يرجى ملاحظة DataEncapsulator و DataCollector هي مكونات التي تقدمها MEF ، ولكن الأداء هو جيد جدا.

الطبقة الاستماع() الأسلوب الذي يبدأ خلفية عامل تلقي البيانات.

public void Listen(IDataCollector dataCollector)
{
    this.dataCollector = dataCollector;
    BackgroundWorker worker = new BackgroundWorker();

    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerAsync();
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    port = new SerialPort();

    //Event handlers
    port.ReceivedBytesThreshold = 15;
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);

    ..... remainder of code ...

اقتراحات هي موضع ترحيب!

تحديث: *مجرد ملاحظة سريعة حول ما IDataCollector فئات.ليس هناك طريقة لمعرفة ما إذا كان كل بايت من البيانات التي تم إرسالها قراءة في قراءة واحدة عملية.لذلك في كل مرة قراءة البيانات هو مرت إلى DataColllector التي ترجع صحيح عندما كاملة صالح بروتوكول الرسالة قد وصلت.في هذه الحالة هنا فقط يتحقق مزامنة بايت, طول , crc والذيل بايت.العمل الحقيقي يتم في وقت لاحق من قبل الطبقات الأخرى.*

تحديث 2: أنا محل رمز الآن كما اقترح, ولكن لا يزال هناك شيء خاطئ:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
        var count = port.BytesToRead;

        byte[] buffer = new byte[count];

        var read = port.Read(buffer, 0, count);

        if (DataEncapsulator != null)
            buffer = DataEncapsulator.UnWrap(buffer);

        var response = dataCollector.Collect(buffer);

        if (response != null)
        {
            this.OnDataReceived(response);
        }     
}

ترى هذا يعمل بشكل جيد مع سريع ومستقر الصدد.ولكن OnDataReceived لا يسمى كل وقت استلام البيانات.(راجع MSDN مستندات للمزيد).إذا كان يحصل على بيانات مجزأة و أنت تقرأ فقط مرة واحدة في حالة البيانات يضيع.

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

من الواضح أنني لا أستطيع العودة إلى حلقة في حين حل ، ماذا يمكنني أن أفعل ؟

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

المحلول

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

أنا الغريب ما تحسين الأداء سوف تكسب من خلال خلق هذا المخزن المؤقت في وقت التصميم مع حجم معقول ، ويقول 8K, لذلك لم يكن لديك كل هذا تخصيص الذاكرة و deallocation والتشرذم.من شأنه أن يساعد ؟

private byte[] buffer = new byte[8192];

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(100);
    while (port.BytesToRead > 0)
    {
        var count = port.BytesToRead;

        var read = port.Read(buffer, 0, count);

        // ... more code   
    }
}

بلدي غيرها من القلق مع إعادة تخصيص هذا العازلة على كل التكرار من الحلقة هو أن التخصيص قد لا يكون ضروريا إذا كان المخزن المؤقت هو بالفعل كبيرة بما فيه الكفاية.النظر في ما يلي:

  • حلقة التكرار 1:100 وحدات البايت المتلقاة ؛ تخصيص المخزن المؤقت من 100 بايت
  • حلقة التكرار 2:75 وحدات البايت المتلقاة ؛ تخصيص المخزن المؤقت من 75 بايت

في هذا السيناريو, أنت لا تحتاج إلى إعادة تخصيص المخزن المؤقت ، لأن العازلة من 100 بايت المخصصة في حلقة التكرار 1 هي أكثر من كافية للتعامل مع 75 بايت الواردة في حلقة التكرار 2.ليست هناك حاجة لتدمير 100 بايت العازلة وخلق 75 بايت العازلة.(هذا هو خلافية, بالطبع, إذا كنت فقط بشكل ثابت إنشاء المخزن المؤقت والانتقال بها من حلقة تماما.)

على آخر الظل ، وأود أن أقترح أن DataReceived حلقة تشغل نفسها فقط مع استقبال البيانات.أنا لست متأكدا ما تلك MEF مكونات يفعلون ، ولكن السؤال إذا كان العمل الذي ينبغي القيام به في بيانات استقبال حلقة.هل من الممكن البيانات الواردة على نوع من الانتظار MEF المكونات يمكن أن تلتقط لهم هناك ؟ أنا مهتم في الحفاظ على DataReceived حلقة سريعا بقدر الإمكان.ربما تلقى البيانات يمكن وضعها على قائمة الانتظار بحيث يمكن أن تعود إلى العمل تلقي المزيد من البيانات.يمكنك انشاء موضوع آخر, ربما, مشاهدة البيانات وصوله إلى الانتظار قد MEF مكونات التقاط البيانات من هناك و القيام بعملهم من هناك.التي قد تكون أكثر الترميز ، ولكن ذلك قد يساعد بيانات استقبال حلقة تكون استجابة ممكن.

نصائح أخرى

وأنها يمكن أن تكون بسيطة جدا...

أما استخدام DataReceived معالج ولكن دون حلقة وبالتأكيد دون النوم () ، قراءة البيانات جاهزة و دفعها في مكان ما (إلى طابور أو MemoryStream),

أو

بداية موضوع (BgWorker) و لا (حجب) serialPort1.قراءة(...), و مرة أخرى, دفع أو تجميع البيانات التي تحصل عليها.

تحرير:

من ما نشر أود أن أقول:إسقاط eventhandler و مجرد قراءة بايت داخل Dowork().أن له فائدة يمكنك تحديد مقدار البيانات التي تريد طالما أنها (الكثير) أصغر من ReadBufferSize.

Edit2 ، فيما يتعلق Update2:

سوف يكون لا يزال أفضل بكثير من مع حلقة while داخل BgWorker, لا تستخدم هذا الحدث على الإطلاق.طريقة بسيطة:

byte[] buffer = new byte[128];  // 128 = (average) size of a record
while(port.IsOpen && ! worker.CancelationPending)
{
   int count = port.Read(buffer, 0, 128);
   // proccess count bytes

}

الآن ربما السجلات الخاصة بك هي متغير الحجم و لا لا أريد أن انتظر القادم 126 بايت في واحدة كاملة.يمكنك ضبط هذا عن طريق تقليل حجم المخزن المؤقت أو مجموعة ReadTimeOut.الحصول على جيد جدا الحبيبات هل يمكن استخدام المنفذ.ReadByte().منذ أن يقرأ من ReadBuffer انها ليست حقا أي أبطأ.

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

private byte[] buffer = new byte[8192]; 
var index = 0;
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{    
    index += port.Read(buffer, index, port.BytesToRead);
} 

void WriteDataToFile()
{
    binaryWriter.Write(buffer, 0, index); 
    index = 0;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top