يقوم تطبيق BackgroundWorker OnWorkCompleted بطرح استثناء عبر مؤشرات الترابط

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

سؤال

لدي UserControl بسيط لترحيل قاعدة البيانات، يستخدم وحدة تحكم لإجراء مكالمات DAL الفعلية.أنا استخدم أ BackgroundWorker لأداء رفع الأثقال، وعلى OnWorkCompleted إذا قمت بإعادة تمكين بعض الأزرار، قم بتغيير أ TextBox.Text الخاصية ورفع حدث للنموذج الأصلي.

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

في الوقت الحالي أضفت شيكًا لـ InvokeRequired في المعالج هناك، ولكن ليس بيت القصيد OnWorkCompleted هو أن يسمى على الموضوع الرئيسي؟لماذا لا تعمل كما هو متوقع؟

يحرر:

لقد تمكنت من تضييق نطاق المشكلة إلى Arcgis و BackgroundWorker.لدي الحل التالي الذي يضيف أمرًا إلى arcmap، والذي يفتح ملفًا بسيطًا Form1 مع زرين.

الزر الأول يعمل أ BackgroundWorker الذي ينام لمدة 500 مللي ثانية ويقوم بتحديث العداد.في ال RunWorkerCompleted الطريقة التي يتحقق منها InvokeRequired, ، ويقوم بتحديث العنوان لإظهار ما إذا كانت الطريقة تعمل في الأصل داخل الخيط الرئيسي أو الخيط العامل.الزر الثاني يفتح للتو Form2, ، الذي لا يحتوي على شيء.

في البداية، جميع المكالمات إلى RunWorkerCompletedare يتم إجراؤها داخل الخيط الرئيسي (كما هو متوقع - هذا هو الهدف من طريقة RunWorkerComplete، على الأقل حسب ما أفهمه من MSDN على BackgroundWorker)

بعد الفتح والإغلاق Form2, ، ال RunWorkerCompleted يتم استدعاؤه دائمًا على مؤشر ترابط العامل.أريد أن أضيف أنه يمكنني فقط ترك هذا الحل للمشكلة كما هو (تحقق من InvokeRequired في ال RunWorkerCompleted طريقة)، ولكن أريد أن أفهم لماذا يحدث ذلك ضد توقعاتي.في الكود "الحقيقي" الخاص بي، أود دائمًا أن أعرف أن RunWorkerCompleted يتم استدعاء الأسلوب على الموضوع الرئيسي.

تمكنت من تحديد المشكلة في form.Show(); أمر في بلدي BackgroundTesterBtn - إذا كنت تستخدم ShowDialog() بدلا من ذلك، ليس لدي أي مشكلة (RunWorkerCompleted يعمل دائمًا على الموضوع الرئيسي).أنا بحاجة إلى استخدام Show() في مشروع ArcMap الخاص بي، بحيث لا يكون المستخدم مرتبطًا بالنموذج.

لقد حاولت أيضًا إعادة إنتاج الخطأ في مشروع WinForms عادي.لقد أضفت مشروعًا بسيطًا يفتح النموذج الأول بدون ArcMap، لكن في هذه الحالة لم أتمكن من إعادة إنتاج الخطأ - RunWorkerCompleted ركض على الموضوع الرئيسي، سواء كنت تستخدم Show() أو ShowDialog(), ، قبل وبعد الافتتاح Form2.لقد حاولت إضافة نموذج ثالث ليكون بمثابة النموذج الرئيسي قبل نموذجي Form1, ، لكنها لم تغير النتيجة.

هنا هو sln البسيط الخاص بي (VS2005sp1) - فهو يتطلب

ESRI.ArcGIS.ADF(9.2.4.1420)

ESRI.ArcGIS.ArcMapUI(9.2.3.1380)

ESRI.ArcGIS.SystemUI (9.2.3.1380)

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

المحلول

ويبدو وكأنه علة:

http://connect.microsoft.com/VisualStudio/feedback /ViewFeedback.aspx؟FeedbackID=116930

http://thedatafarm.com/devlifeblog/archive/2005 /12/21/39532.aspx

ولذا أقترح استخدام واقية من الرصاص (شبة الكود):

if(control.InvokeRequired)
  control.Invoke(Action);
else
  Action()

نصائح أخرى

ليس بيت القصيد من OnWorkCompleted هو أن يسمى على الموضوع الرئيسي؟لماذا لا تعمل كما هو متوقع؟

لا ليس كذلك.
لا يمكنك تشغيل أي شيء قديم على أي موضوع قديم.المواضيع ليست كائنات مهذبة يمكنك ببساطة أن تقول "قم بتشغيل هذا، من فضلك".

النموذج العقلي الأفضل للخيط هو قطار الشحن.وبمجرد أن يبدأ، فإنه يسير في مساره الخاص.لا يمكنك تغيير مساره أو إيقافه.إذا كنت تريد التأثير عليه، عليك إما الانتظار حتى يصل إلى محطة القطار التالية (على سبيل المثال:اطلب منه التحقق يدويًا من بعض الأحداث)، أو إخراجه عن مساره (Thread.Abort واستثناءات CrossThread لها نفس العواقب مثل إخراج القطار عن مساره ...احذر!).

ضوابط Winforms نوعا ما دعم هذا السلوك (لديهم Control.BeginInvoke والتي تتيح لك تشغيل أي وظيفة على مؤشر ترابط واجهة المستخدم)، ولكن هذا يعمل فقط لأن لديهم ربطًا خاصًا في مضخة رسائل واجهة مستخدم Windows وكتابة بعض المعالجات الخاصة.لتطبيق القياس المذكور أعلاه، يقوم قطارهم بالتحقق من المحطة ويبحث عن اتجاهات جديدة بشكل دوري، ويمكنك استخدام هذه المنشأة لنشر الاتجاهات الخاصة بك.

ال BackgroundWorker تم تصميمه ليكون للأغراض العامة (لا يمكن ربطه بواجهة المستخدم الرسومية الخاصة بنظام التشغيل Windows) لذا لا يمكنه استخدام النوافذ Control.BeginInvoke سمات.يجب أن يفترض أن مؤشر الترابط الرئيسي الخاص بك هو "قطار" لا يمكن إيقافه يقوم بعمله الخاص، لذا يجب أن يتم تشغيل الحدث المكتمل في مؤشر ترابط العامل أو لا يعمل على الإطلاق.

ومع ذلك، أثناء استخدامك winforms، في ملف OnWorkCompleted المعالج، يمكنك الحصول على النافذة للتنفيذ آخر رد الاتصال باستخدام BeginInvoke الوظيفة التي ذكرتها أعلاه.مثله:

// Assume we're running in a windows forms button click so we have access to the 
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
    var b = new BackgroundWorker();
    b.DoWork += ... blah blah

    // attach an anonymous function to the completed event.
    // when this function fires in the worker thread, it will ask the form (this)
    // to execute the WorkCompleteCallback on the UI thread.
    // when the form has some spare time, it will run your function, and 
    // you can do all the stuff that you want
    b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
    b.RunWorkerAsync(); // GO!
}

void WorkCompleteCallback()
{
    Button.Enabled = false;
    //other stuff that only works in the UI thread
}

ولا تنسى هذا أيضاً:

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

ال BackgroundWorker يتحقق مما إذا كان مثيل المفوض يشير إلى فئة تدعم الواجهة ISynchronizeInvoke.ربما لا تقوم طبقة DAL الخاصة بك بتنفيذ تلك الواجهة.عادة، سوف تستخدم BackgroundWorker على Form, ، والذي يدعم تلك الواجهة.

في حال كنت ترغب في استخدام BackgroundWorker من طبقة DAL وتريد تحديث واجهة المستخدم من هناك، لديك ثلاثة خيارات:

  • ستبقى تتصل بـ Invoke طريقة
  • تنفيذ الواجهة ISynchronizeInvoke على فئة DAL، وإعادة توجيه المكالمات يدويًا (إنها ثلاث طرق وخاصية فقط)
  • قبل استدعاء BackgroundWorker (لذلك، على موضوع واجهة المستخدم)، للاتصال SynchronizationContext.Current ولحفظ مثيل المحتوى في متغير مثيل.ال SynchronizationContext سوف يعطيك بعد ذلك Send الطريقة، والتي سوف تفعل بالضبط ما Invoke يفعل.

والنهج الأفضل لتجنب المشاكل مع عبر خيوط في واجهة المستخدم الرسومية هو <وأ href = "http://www.lostechies.com/blogs/gabrielschenker/archive/2009/01/23/synchronizing-calls-to- و-UI-في-متعددة الخيوط application.aspx "يختلط =" نوفولو noreferrer "> استخدام SynchronizationContext .

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