it-swarm.asia

Does dispatch_async (dispatch_get_main_queue () ، ^ {...}) ؛ انتظر حتى تنتهي؟

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

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1
    // UI UPDATE 1

    // DATA PROCESSING 2
    // UI UPDATE 2

    // DATA PROCESSING 3
    // UI UPDATE 3
} 

نظرًا لأنه مضيعة للوقت كنت أرغب في القيام بمعالجة البيانات في مؤشر ترابط الخلفية ، باستخدام ،

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

ولكن نظرًا لأن كلا من معالجة البيانات وتحديثات واجهة المستخدم في نفس الطريقة ، فقد أردت نقل تحديثات واجهة المستخدم فقط في سلسلة الرسائل الرئيسية باستخدام ،

dispatch_async(dispatch_get_main_queue(), ^{

أخيرا طريقتي تبدو مثل هذا ،

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1 
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 1
    });

    /* I expect the control to come here after UI UPDATE 1 */

    // DATA PROCESSING 2
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 2
    });

    /* I expect the control to come here after UI UPDATE 2 */

    // DATA PROCESSING 3
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 3
    });
}

هل هذا حقا العمل؟ هل هذه حقًا ممارسة جيدة؟ ما هي أفضل طريقة لتحقيق ذلك؟

ملاحظة كل هذه العمليات الثلاث مترابطة مع بعضها البعض.


تحرير: آسف يا شباب. لقد غاب عن سطر في الرمز أعلاه. بلدي رمز الفعلي يشبه هذا.

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // DATA PROCESSING 1 
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 1
        });

        /* I expect the control to come here after UI UPDATE 1 */

        // DATA PROCESSING 2
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 2
        });

        /* I expect the control to come here after UI UPDATE 2 */

        // DATA PROCESSING 3
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 3
        });
    });
}

مرة أخرى ، أعتذر حقًا عن الالتباس.

44
EmptyStack

لا ، لا تنتظر والطريقة التي تقوم بها في هذه العينة ليست ممارسة جيدة.

dispatch_async دائما غير متزامن. إنها مجرد قيامك بتثبيت جميع كتل واجهة المستخدم على نفس قائمة الانتظار حتى يتم تشغيل الكتل المختلفة بالتسلسل ولكن بالتوازي مع رمز معالجة البيانات.

إذا كنت تريد أن ينتظر التحديث ، يمكنك استخدام dispatch_sync في حين أن.

// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
});

نهج آخر سيكون لعشبة enqueueing الكتلة. أنا لا أوصي به لمستويات متعددة بالرغم من ذلك.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Background work

    dispatch_async(dispatch_get_main_queue(), ^{
        // Update UI

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // Background work

            dispatch_async(dispatch_get_main_queue(), ^{
                // Update UI
            });
        });
    });
});

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

108
David Rönnqvist

يجب عليك إرسال قائمة الانتظار الرئيسية الخاصة بك في الكتلة التي تدير الحساب. على سبيل المثال (أقوم هنا بإنشاء قائمة انتظار للإرسال ولا تستخدم قائمة انتظار عامة):

dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});

بالطبع ، إذا قمت بإنشاء قائمة انتظار ، فلا تنس أن dispatch_release إذا كنت تستهدف إصدار iOS قبل الإصدار 6.0.

12
StatusReport

تقوم doCalculationsAndUpdateUIs بمعالجة البيانات وإرسال تحديثات واجهة المستخدم إلى قائمة الانتظار الرئيسية. أفترض أنك أرسلت doCalculationsAndUpdateUIs إلى قائمة انتظار في الخلفية عندما قمت بالاتصال بها لأول مرة.

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

وبالتالي قد يبدو مثل:

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

        // DATA PROCESSING 1 

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 1
        });

        /* I expect the control to come here after UI UPDATION 1 */

        // DATA PROCESSING 2

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 2
        });

        /* I expect the control to come here after UI UPDATION 2 */

        // DATA PROCESSING 3

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 3
        });
    });
}

فيما يتعلق بما إذا كنت ترسل تحديثات واجهة المستخدم الخاصة بك بشكل غير متزامن مع dispatch_async (حيث ستعمل عملية الخلفية لا انتظر تحديث واجهة المستخدم) أو بشكل متزامن مع dispatch_sync (حيث سوف انتظر تحديث واجهة المستخدم) ، والسؤال هو لماذا هل ترغب في القيام بذلك بشكل متزامن: هل تريد حقًا إبطاء عملية الخلفية كما ينتظر تحديث واجهة المستخدم ، أو ترغب في استمرار عملية الخلفية أثناء إجراء تحديث واجهة المستخدم.

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


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

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

8
Rob

إذا كنت تريد تشغيل عملية قائمة انتظار مستقلة واحدة ولم تكن مهتمًا بالعمليات المتزامنة الأخرى ، يمكنك استخدام قائمة الانتظار المتزامنة العامة:

dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

هذا سيعيد قائمة انتظار متزامنة مع الأولوية المعطاة كما هو موضح في الوثائق:

DISPATCH_QUEUE_PRIORITY_HIGH سيتم تشغيل العناصر المرسلة إلى قائمة الانتظار بأولوية قصوى ، أي سيتم جدولة قائمة الانتظار للتنفيذ قبل أي أولوية افتراضية أو قائمة انتظار ذات أولوية منخفضة.

DISPATCH_QUEUE_PRIORITY_DEFAULT سيتم تشغيل العناصر المرسلة إلى قائمة الانتظار عند الأولوية الافتراضية ، أي سيتم جدولة قائمة الانتظار للتنفيذ بعد جدولة جميع قوائم الانتظار ذات الأولوية العالية ، ولكن قبل جدولة أي قوائم انتظار ذات أولوية منخفضة.

DISPATCH_QUEUE_PRIORITY_LOW سيتم تشغيل العناصر المرسلة إلى قائمة الانتظار بأولوية منخفضة ، بمعنى أنه سيتم جدولة قائمة الانتظار للتنفيذ بعد جدولة جميع قوائم الانتظار الافتراضية ذات الأولوية العالية.

DISPATCH_QUEUE_PRIORITY_BACKGROUND سيتم تشغيل العناصر المرسلة إلى قائمة الانتظار في أولوية الخلفية ، أي سيتم جدولة قائمة الانتظار للتنفيذ بعد جدولة جميع قوائم الانتظار ذات الأولوية العليا وسيعمل النظام على تشغيل عناصر في قائمة الانتظار هذه في سلسلة رسائل مع حالة خلفية وفقًا لـ setpriority (2) ( أي القرص I/O هو خنق وتحديد أولوية جدولة مؤشر الترابط إلى أدنى قيمة).

2
Ashokios
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});
1
Moin Shirazi

لا ، لن تنتظر.

يمكنك استخدام performSelectorOnMainThread:withObject:waitUntilDone:.

1
Wain

الممارسة الجيدة هي: مجموعات الإرسال

dispatch_group_t imageGroup = dispatch_group_create();

dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image successfully uploaded to S3
    dispatch_group_leave(imageGroup);
}];

dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image url updated
    dispatch_group_leave(imageGroup);
}];

dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
    // We get here when both tasks are completed
});
0
ChavirA