سؤال

لقد كنت أتابع الإعلان الجديد بخصوص الجديد async ميزة ستكون في C# 5.0. لدي فهم أساسي لأسلوب تمرير الاستمرار والتحول الذي يقدمه برنامج التحويل البرمجي الجديد C# إلى رمز مثل هذا المقتطف من منشور إريك ليبرت:

async void ArchiveDocuments(List<Url> urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

أعلم أن بعض اللغات تنفذ الاستمرارية أصلاً ، عبر استدعاء الاتصال بالتواصل (callcc) ، لكنني لا أفهم حقًا كيف يعمل ذلك أو ما يفعله بالضبط.

إذن هذا هو السؤال: إذا أندرس وآخرون. قررت أن تعض الرصاصة وتنفيذها فقط callcc في C# 5.0 بدلاً من async/await حالة خاصة ، كيف سيبدو المقتطف أعلاه؟

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

المحلول

الإجابة الأصلية:

سؤالك ، كما أفهمه ، هو "ماذا لو كان بدلاً من تنفيذ" انتظار "على وجه التحديد من أجل عدم التزامن القائم على المهام ، بدلاً من ذلك ، تم تنفيذ عملية تدفق التحكم الأكثر عمومية لاستدعاء الدعوة؟"

حسنًا ، أولاً وقبل كل شيء ، دعونا نفكر فيما تفعله "ينتظر". "في انتظار" يأخذ تعبيرًا عن النوع Task<T>, ، يحصل على انتظار ، ويدعو إلى الانتظار مع الاستمرار الحالي:

await FooAsync()

يصبح بفعالية

var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);

لنفترض الآن أن لدينا عامل callcc التي تأخذ كوسيطة لها طريقة ، وتدعو الطريقة مع الاستمرار الحالي. هذا سيبدو هكذا:

var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;

بعبارات أخرى:

await FooAsync()

ليس أكثر من

callcc FooAsync().GetAwaiter().BeginAwait;

هل هذا الجواب على سؤالك؟


تحديث رقم 1:

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

var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
    awaiter.OnCompleted(somehow get the current continuation);
    // control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.

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

وبالتالي فإن العلاقة بين "الانتظار" و "Callcc" ليست واضحة تمامًا كما كانت في إصدار المعاينة ، ولكن لا يزال من الواضح أننا نقوم بشكل أساسي بعمل CallCC على طريقة "Oncompleted" في الانتظار. نحن فقط لا نفعل Callcc إذا لم يكن علينا ذلك.


تحديث رقم 2:

كما هذا الجواب

https://stackoverflow.com/a/9826822/88656

من Timwi يشير إلى أن دلالات Call/CC وانتظار ليست هي نفسها ؛ يتطلب مكالمة/CC "حقيقية" إما أن نلتقط " بأكمله استمرار طريقة بما في ذلك مكدس المكالمات بالكامل, ، أو ما يعادله يتم إعادة كتابة البرنامج بأكمله في نمط تمرير استمرار.

ميزة "Await" تشبه "مكالمة تعاونية/CC" ؛ الاستمرار يلتقط فقط "ما هو التيار طريقة إعادة المهام على وشك أن تفعل بعد ذلك عند نقطة الانتظار؟ "إذا المتصل من بين طريقة إعادة المهام ستقوم بشيء مثير للاهتمام بعد اكتمال المهمة ، يكون من المجاني التسجيل استمرارها كما استمرار المهمة.

نصائح أخرى

أنا لست خبيراً في الاستمرارية ، لكنني سأطرح طعنة في شرح الفرق بين Async/AsaIt و Call/CC. بالطبع يفترض هذا التفسير أنني أفهم الاتصال/CC و Async/Achait ، وهو ما لست متأكدًا من ذلك. ومع ذلك ، هنا يذهب ...

باستخدام C# 'Async' ، فأنت تطلب من المترجم إنشاء نسخة خاصة من تلك الطريقة المحددة التي تتفهم كيفية زجاجة حالتها في بنية data heap ، بحيث يمكن "إزالتها من المكدس الحقيقي" واستأنفها لاحقًا. ضمن السياق Async ، فإن "Ayait" مثل "Call/CC" من حيث أنه يستخدم الكائنات التي تم إنشاؤها بواسطة برنامج التحويل البرمجي لزجاجة الحالة والخروج من "المجموعة الحقيقية" حتى تكمل المهمة. ومع ذلك ، نظرًا لأن إعادة كتابة المترجم لطرق غير متزامنة تسمح للدولة بالتعبئة ، فإن الانتظار لا يمكن استخدامه إلا في سياق غير متزامن.

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

يمكن أن تنشأ بعض المشكلات الصعبة إذا كانت هناك مكالمات إلى وظائف خارجية (فكر في Dllimport) متداخلة على المكدس. أظن أن هذا هو السبب في أنهم ذهبوا مع تنفيذ Async/انتظار.

http://www.madore.org/~david/computers/callcc.html


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

كيندا لا طائل منه أود أن أقول. غالبًا ما يقوم المترجمون المترجمون المخططون بتنفيذ CALL/CC مع آلة الولاية التي تلتقط الدولة المحلية إلى الكومة. الذي بالضبط ما يفعله C# 5.0 (أو بشكل صحيح بشكل صحيح C# 2.0). أنهم فعلت تنفيذ Call/CC ، التجريد الذي توصلوا إليه هو أنيقة للغاية.

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