سؤال

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

int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       TotalFound++;
       buffer.post(file);
       lblProgress.Text = String.Format("{0}", TotalFound);
    }
}, options);

الكود أعلاه يجمد النموذج الخاص بي، حتى أنني أستخدم "TaskScheduler.FromCurrentSynchronizationContext"، لماذا؟لأنه عندما أستخدم الكود الموجود أسفل النموذج الخاص بي يتم تحديثه بشكل جيد

DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       Task.Factory.StartNew(() => {
          TotalFound++;
          buffer.Post(file);
       }).ContinueWith(uiTask => {
          lblProgress.Text = String.Format("{0}", TotalFound);
       },CancellationToken.None, TaskContinuationOptions.None, uiScheduler);           
    }
});

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

تحياتي الطيبة ، مارتين

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

المحلول

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

من ناحية أخرى، عليك تشغيل lblProgress.Text على موضوع واجهة المستخدم.

لست متأكدا من أنه يجب عليك تعيين lblProgress.Text مباشرة في هذا الكود، يبدو لي وكأنه اقتران محكم جدًا.ولكن إذا كنت تريد القيام بذلك، أعتقد أنه يجب عليك تشغيل هذا السطر فقط في مؤشر ترابط واجهة المستخدم:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        await Task.Factory.StartNew(
            () => lblProgress.Text = String.Format("{0}", TotalFound),
            CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    }
});

ولكن الحل الأفضل سيكون إذا قمت بذلك GetFileFromLargeFile() غير متزامن وتأكد من أنه لا يقوم بأي إجراءات طويلة الأمد على سلسلة واجهة المستخدم (ConfigureAwait(false) يمكن أن تساعدك في ذلك).إذا قمت بذلك، فإن رمز ActionBlock يمكن تشغيله على مؤشر ترابط واجهة المستخدم دون تجميد واجهة المستخدم الخاصة بك:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = await GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        lblProgress.Text = String.Format("{0}", TotalFound)
    }
}, options);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top