حول Corutinism التنافسية (استخدام البرمجة التفاعلية كمثال)

1 المقدمة


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

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

وماذا تقدم البرمجة الحالية بالإضافة إلى ما هو معروف بالفعل قبل 40 عامًا على الأقل؟ في "مظهري الصقيع" - لا شيء. تسمى Coroutines ، كما اتضح ، الآن coroutines أو حتى goroutines ؛ يبدو أن المصطلحات التزامن والمنافسة تدخل في الذهول ، ليس فقط المترجمين. ولا توجد مثل هذه الأمثلة. على سبيل المثال ، ما الفرق بين البرمجة التفاعلية (RP) وبرمجة الأحداث أو البث؟ أي من الفئات و / أو التصنيفات المعروفة التي تندرج فيها؟ يبدو أن لا أحد مهتم بهذا ، ولا أحد يستطيع توضيح ذلك. أو يمكنك تصنيفها الآن بالاسم؟ ثم ، في الواقع ، فإن coroutines و coroutines هي أشياء مختلفة ، والبرمجة المتوازية ملزمة ببساطة بالاختلاف عن التنافسية. ماذا عن آلات الدولة؟ ما نوع هذه التقنية المعجزة؟

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

2. البرمجة التفاعلية وكل شيء وكل شيء وكل شيء


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

لذلك ، في [2] ، تم النظر في مثال لبرنامج تفاعلي ، يظهر رمزه في القائمة 1.

الإدراج 1. رمز البرنامج التفاعلي
1. 1 = 2 
2. 2 = 3 
3. 3 = 1 + 2 
4.  1, 2, 3 
5. 1 = 4 
6.  1, 2, 3


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

وقد أدى هذا الغموض في تفسير المدونة إلى حقيقة أنه لا يمكن على الفور "تقطيعه". ولكن بعد ذلك ، كما يحدث في كثير من الأحيان ، اتضح أن كل شيء أبسط بكثير مما توقعه المرء. يوضح الشكل 1 رسمين هيكليين ، نأمل أن يتوافق مع الهيكل ويشرح تشغيل المثال. في المخطط العلوي ، تنظم الكتل X1 و X2 إدخال البيانات ، مما يشير إلى كتلة X3 حول تغييرها. هذا الأخير يقوم بالجمع ويسمح للكتلة Pr بطباعة القيم الحالية للمتغيرات. بعد طباعته ، تشير كتلة Pr إلى كتلة X3 ، علاوة على ذلك ، له وفقط له أنه مستعد لطباعة قيم جديدة.

تين. 1. نموذجان هيكليان للمثال
image

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

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

في التين. يقدم 2 نماذج خوارزمية توضح بالتفصيل الهيكل [الخوارزمي] الداخلي لكتل ​​الدارات. يتم تمثيل النموذج العلوي بشبكة من automata ، حيث يكون كل من automata نموذجًا خوارزميًا لكتلة منفصلة. تتوافق الوصلات بين الأتمتة الموضحة بأقواس منقطعة مع وصلات الدائرة. يصف نموذج أوتوماتيكي فردي خوارزمية تشغيل مخطط كتلة تتكون من كتلة واحدة (انظر كتلة Pr منفصلة في الشكل 1).

تين. 2. النماذج الخوارزمية للمخططات الهيكلية
image

تتطابق آليتا X1 و X2 (أسماء الآليين والكتل مع أسماء متغيراتها) ، وتكشف التغييرات ، وإذا كان automaton X3 جاهزًا لإجراء عملية الإضافة (في الحالة "s0") ، انتقل إلى الحالة "s1" ، مع تذكر القيمة الحالية للمتغير. تقوم آلة X3 ، بعد الحصول على إذن بدخول الحالة "s1" ، بإجراء عملية الإضافة ، وإذا لزم الأمر ، تنتظر اكتمال طباعة المتغيرات. "آلة الطباعة" Pr ، بعد الانتهاء من الطباعة ، تعود إلى الحالة الأولية "p0" ، حيث تنتظر الأمر التالي. لاحظ أن حالتها "p1" تبدأ سلسلة من التحولات العكسية - الأوتوماتيكي X3 إلى الحالة "s0" ، و X1 و X2 إلى الحالة "s0". بعد ذلك ، يتم تحليل البيانات المدخلة ، ثم يتم تكرار جمعها وطبعها اللاحق.

بالمقارنة مع شبكة automaton ، فإن خوارزمية Pr automaton المنفصلة بسيطة للغاية ، لكننا نلاحظ أنها تقوم بنفس المهمة وربما أسرع. تكشف المسندات عن تغير في المتغيرات. إذا حدث هذا ، فسيتم الانتقال إلى الحالة "p1" مع بداية الإجراء y1 (انظر الشكل 2) ، الذي يلخص القيم الحالية للمتغيرات ، مع تذكرها. بعد ذلك ، عند الانتقال غير المشروط من الحالة "p1" إلى الحالة "p0" ، يطبع الإجراء y2 المتغيرات. بعد ذلك ، تعود العملية إلى تحليل بيانات الإدخال. يظهر رمز تنفيذ أحدث طراز في القائمة 2.

القائمة 2. تنفيذ آلية العلاقات العامة
#include "lfsaappl.h"
#include "fsynch.h"
extern LArc TBL_PlusX3[];
class FPlusX3 : public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FPlusX3(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FPlusX3(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_PlusX3, strNam, nullptr, pCVFL) { }

    CVar *pVarY;        		// 
    CVar *pVarX1;        		// 
    CVar *pVarX2;        		// 
    CVar *pVarX3;        		// 
    CVar *pVarStrNameX1;		//   X1
    CVar *pVarStrNameX2;		//   X2
    CVar *pVarStrNameX3;		//   X3
protected:
    int x1(); int x2();
    int x12() { return pVarX1 != nullptr && pVarX2 && pVarX3; };
    void y1();
    void y12() { FInit(); };
    double dSaveX1{0};
    double dSaveX2{0};
};

#include "stdafx.h"
#include "fplusx3.h"

LArc TBL_PlusX3[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"p0","x12",	"--"),			//
    LArc("p0",		"p1","x1",  "y1"),			//
    LArc("p0",		"p1","x2",  "y1"),			//
    LArc("p1",		"p0","--",  "--"),			//
    LArc()
};

// creating local variables and initialization of pointers
bool FPlusX3::FCreationOfLinksForVariables() {
// creating local variables
    pVarY = CreateLocVar("strY", CLocVar::vtString, "print of output string");			//  
    pVarX1 = CreateLocVar("dX1", CLocVar::vtDouble, "");			//  
    pVarX2 = CreateLocVar("dX2", CLocVar::vtDouble, "");			//  
    pVarX3 = CreateLocVar("dX3", CLocVar::vtDouble, "");			//  
    pVarStrNameX1 = CreateLocVar("strNameX1", CLocVar::vtString, "");			//   
    pVarStrNameX2 = CreateLocVar("strNameX2", CLocVar::vtString, "");			//   
    pVarStrNameX3 = CreateLocVar("strNameX3", CLocVar::vtString, "");			//   
// initialization of pointers
    string str;
    str = pVarStrNameX1->strGetDataSrc();
    if (str != "") { pVarX1 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    str = pVarStrNameX2->strGetDataSrc();
    if (str != "") { pVarX2 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    str = pVarStrNameX3->strGetDataSrc();
    if (str != "") { pVarX3 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    return true;
}

int FPlusX3::x1() { return pVarX1->GetDataSrc() != dSaveX1; }
int FPlusX3::x2() { return pVarX2->GetDataSrc() != dSaveX2; }

void FPlusX3::y1() {
// X3 = X1 + X2
    double dX1 = pVarX1->GetDataSrc(); double dX2 = pVarX2->GetDataSrc();
    double dX3 = dX1 + dX2;
    pVarX3->SetDataSrc(this, dX3);
    dSaveX1 = dX1; dSaveX2 = dX2;
//  1, 2, 3
    QString strX1; strX1.setNum(dX1); QString strX2; strX2.setNum(dX2);
    QString strX3; strX3.setNum(dX3);
    QString qstr = "X1=" + strX1 + ", X2=" + strX2 + ", X3=" + strX3;
    pVarY->SetDataSrc(nullptr, qstr.toStdString(), nullptr);
}


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

ما تبقى من الكود مرتبط بمتطلبات "البيئة الأوتوماتيكية" ، والتي ، كما ألاحظ ، لا يتحدث بها كود المصدر. لذا، فإن FCreationOfLinksForVariables طريقة من الفئة الأساسية إنسان LFsaApplينشئ متغيرات محلية للجهاز ويربطها عندما يتم الإشارة إلى أسماء رمزية لبيئة VKPA لمتغيرات البيئة الأخرى المرتبطة بها على مستوى VKPA. في المرة الأولى التي يبدأ عند إنشاء آلية ، ثم في إطار طريقة FInit (انظر الخطوة y12) ، لأن ليست كل الروابط معروفة عند إنشاء كائن. سيكون الجهاز في حالة "st" حتى يتم تهيئة كافة الارتباطات الضرورية التي تتم تهيئة عمليات التحقق الأصلية x12. إرجاع مرجع إلى متغير ، إذا تم إعطاء اسمه ، أسلوب GetAddressVar.

لإزالة الأسئلة المحتملة ، نقدم رمز شبكة الاتصال الآلي. يظهر في القائمة 3 ويتضمن رمز لثلاث فئات من الأوتوماتون. على أساسها يتم إنشاء العديد من الكائنات التي تتوافق مع المخطط الهيكلي للشبكة الموضحة في الشكل. 1. لاحظ أن الكائنات X1 و X2 مشتقة من الفئة العامة FSynch.

قائمة 3. فئات الشبكة الآلي
#include "lfsaappl.h"

extern LArc TBL_Synch[];
class FSynch : public LFsaAppl
{
public:
    double dGetData() { return pVarX->GetDataSrc(); };
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FSynch(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FSynch(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_Synch, strNam, nullptr, pCVFL) { }

    CVar *pVarX;			// 
    CVar *pVarStrNameX;		//   
    CVar *pVarStrNameObject;//  -
    LFsaAppl *pL {nullptr};
protected:
    int x1() { return pVarX->GetDataSrc() != dSaveX; }
    int x2() { return pL->FGetState() == "s1"; }
    int x12() { return pL != nullptr; };
    void y1() { dSaveX = pVarX->GetDataSrc(); }
    void y12() { FInit(); };
    double dSaveX{0};
};

#include "stdafx.h"
#include "fsynch.h"

LArc TBL_Synch[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s0","x12",	"y1"),			//
    LArc("s0",		"s1","x1",  "y1"),			//
    LArc("s1",		"s0","x2",	"--"),			//
    LArc()
};

// creating local variables and initialization of pointers
bool FSynch::FCreationOfLinksForVariables() {
// creating local variables
    pVarX = CreateLocVar("x", CLocVar::vtDouble, " ");
    pVarStrNameX = CreateLocVar("strNameX1", CLocVar::vtString, "name of external input variable(x1)");			//   
    pVarStrNameObject = CreateLocVar("strNameObject", CLocVar::vtString, "name of function");                   //  
// initialization of pointers
    string str;
    if (pVarStrNameX) {
        str = pVarStrNameX->strGetDataSrc();
        if (str != "") { pVarX = pTAppCore->GetAddressVar(str.c_str(), this);	}
    }
    str = pVarStrNameObject->strGetDataSrc();
    if (str != "") { pL = FGetPtrFsaAppl(str);	}
    return true;
}

#include "lfsaappl.h"
#include "fsynch.h"

extern LArc TBL_X1X2X3[];
class FX1X2X3 : public LFsaAppl
{
public:
    double dGetData() { return pVarX3->GetDataSrc(); };
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FX1X2X3(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FX1X2X3(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_X1X2X3, strNam, nullptr, pCVFL) { }

    CVar *pVarX1{nullptr};			//
    CVar *pVarX2{nullptr};			//
    CVar *pVarX3{nullptr};			//
    CVar *pVarStrNameFX1;		//  X1
    CVar *pVarStrNameFX2;		//  X2
    CVar *pVarStrNameFPr;		//  Pr
    CVar *pVarStrNameX3;		//   
    FSynch *pLX1 {nullptr};
    FSynch *pLX2 {nullptr};
    LFsaAppl *pLPr {nullptr};
protected:
    int x1() { return pLX1->FGetState() == "s1"; }
    int x2() { return pLX2->FGetState() == "s1"; }
    int x3() { return pLPr->FGetState() == "p1"; }
    int x12() { return pLPr != nullptr && pLX1 && pLX2 && pVarX3; };
    void y1() { pVarX3->SetDataSrc(this, pLX1->dGetData() + pLX2->dGetData()); }
    void y12() { FInit(); };
};
#include "stdafx.h"
#include "fx1x2x3.h"

LArc TBL_X1X2X3[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s0","x12",	"--"),			//
    LArc("s0",		"s1","x1",  "y1"),			//
    LArc("s0",		"s1","x2",  "y1"),			//
    LArc("s1",		"s0","x3",	"--"),			//
    LArc()
};
// creating local variables and initialization of pointers
bool FX1X2X3::FCreationOfLinksForVariables() {
// creating local variables
    pVarX3 = CreateLocVar("x", CLocVar::vtDouble, " ");
    pVarStrNameFX1 = CreateLocVar("strNameFX1", CLocVar::vtString, "");
    pVarStrNameFX2 = CreateLocVar("strNameFX2", CLocVar::vtString, "");
    pVarStrNameFPr = CreateLocVar("strNameFPr", CLocVar::vtString, "");
    pVarStrNameX3 = CreateLocVar("strNameX3", CLocVar::vtString, "");
// initialization of pointers
    string str; str = pVarStrNameFX1->strGetDataSrc();
    if (str != "") { pLX1 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX2->strGetDataSrc();
    if (str != "") { pLX2 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFPr->strGetDataSrc();
    if (str != "") { pLPr = FGetPtrFsaAppl(str);	}
    return true;
}
#include "lfsaappl.h"
#include "fsynch.h"

extern LArc TBL_Print[];
class FX1X2X3;
class FPrint : public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FPrint(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FPrint(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_Print, strNam, nullptr, pCVFL) { }

    CVar *pVarY;        		// 
    CVar *pVarStrNameFX1;		//    X1
    CVar *pVarStrNameFX2;		//    X2
    CVar *pVarStrNameFX3;		//    X3
    FSynch *pLX1 {nullptr};     //    X1
    FSynch *pLX2 {nullptr};     //    X2
    FX1X2X3 *pLX3 {nullptr};    //    X3
protected:
    int x1();
    int x12() { return pLX3 != nullptr && pLX1 && pLX2 && pLX3; };
    void y1();
    void y12() { FInit(); };
};
#include "stdafx.h"
#include "fprint.h"
#include "fx1x2x3.h"

LArc TBL_Print[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"p0","x12",	"--"),			//
    LArc("p0",		"p1","x1",  "y1"),			//
    LArc("p1",		"p0","--",	"--"),			//
    LArc()
};
// creating local variables and initialization of pointers
bool FPrint::FCreationOfLinksForVariables() {
// creating local variables
    pVarY = CreateLocVar("strY", CLocVar::vtString, "print of output string");			//  
    pVarStrNameFX1 = CreateLocVar("strNameFX1", CLocVar::vtString, "name of external input object(x1)");			//   
    pVarStrNameFX2 = CreateLocVar("strNameFX2", CLocVar::vtString, "name of external input object(x2)");			//   
    pVarStrNameFX3 = CreateLocVar("strNameFX3", CLocVar::vtString, "name of external input object(pr)");			//   
// initialization of pointers
    string str;
    str = pVarStrNameFX1->strGetDataSrc();
    if (str != "") { pLX1 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX2->strGetDataSrc();
    if (str != "") { pLX2 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX3->strGetDataSrc();
    if (str != "") { pLX3 = (FX1X2X3*)FGetPtrFsaAppl(str);	}
    return true;
}

int FPrint::x1() { return pLX3->FGetState() == "s1"; }

void FPrint::y1() {
    QString strX1; strX1.setNum(pLX1->dGetData());
    QString strX2; strX2.setNum(pLX2->dGetData());
    QString strX3; strX3.setNum(pLX3->dGetData());
    QString qstr = "X1=" + strX1 + ", X2=" + strX2 + ", X3=" + strX3;
    pVarY->SetDataSrc(nullptr, qstr.toStdString(), nullptr);
}


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

لذلك ، بالنظر إلى القائمة 3 ، عليك أن تتخيل أن عدد الفئات لا يرتبط مباشرة بعدد الكائنات المقابلة في البرنامج الموازي. لا يعكس الرمز العلاقة بين الكائنات ، ولكنه يحتوي على الآليات التي تنشئها. لذا ، تحتوي فئة FSynch على مؤشر pL لكائن من النوعLFsaAppl . يتم تحديد اسم هذا الكائن بواسطة متغير محلي ، والذي في بيئة VKPa سيتوافق مع متغير آلي بالاسم strNameObject . يعد المؤشر ضروريًا لاستخدام طريقة FGetState لمراقبة الحالة الحالية لكائن أوتوماتي من نوع FSynch (انظر الرمز الأصلي x2). تحتوي المؤشرات المشابهة للكائنات ، ومتغيرات تحديد أسماء الكائنات ، والمسندات اللازمة لتنظيم العلاقات على فئات أخرى.

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

3. لتحليل البرامج الموازية


حول عمل البرنامج الموازي ، ما لم يكن بسيطًا بشكل متوازي متسلسل ، فمن الصعب جدًا جدًا قول شيء ملموس. الشبكة المعتبرة من automata ليست استثناء. بعد ذلك ، سنرى ذلك ، ونفهم ما يمكن توقعه منه.

يظهر الشكل الآلي والشبكة التي تم بناؤها من أجلها في الشكل. 3. من الشبكة في الشكل. 2 ، بالإضافة إلى إعادة تسمية عناصرها - الأتمتة ، وإشارات الإدخال والإخراج ، تتميز بعدم وجود "آلة طباعة" من المتغيرات. هذا الأخير ليس ضروريًا لتشغيل الشبكة ، وتسمح لك إعادة التسمية باستخدام عملية التكوين لبناء المحرك الناتج. بالإضافة إلى ذلك ، لإنشاء أسماء أقصر ، تم إدخال التشفير ، على سبيل المثال ، عندما يتم تمثيل الحالة "a0" من automaton A بالرمز "0" و "a1" بالرمز "1". وبالمثل للآلات الأخرى. في هذه الحالة ، يتم تعيين حالة مكون الشبكة ، على سبيل المثال ، "a1b0c1" بالاسم "101". وبالمثل ، يتم تشكيل الأسماء لجميع حالات مكونات الشبكة ، والتي يتم تحديد عددها من خلال منتج حالات مكونات المكون الآلي.

تين. 3. الشبكة الآلية الناتجة
image

يمكن بالطبع أن يتم حساب المحرك الآلي بطريقة رسمية بحتة ، ولكن لهذا نحن بحاجة إلى "آلة حاسبة" مناسبة. ولكن إذا لم يكن كذلك ، فيمكنك استخدام خوارزمية بديهية بسيطة إلى حد ما. في إطارها ، يتم تسجيل حالة مكون واحد أو آخر للشبكة ومن ثم ، من خلال فرز جميع حالات الإدخال الممكنة ، يتم تحديد حالات المكون الهدف من خلال "مقابض". لذا ، بعد إصلاح الحالة "000" المطابقة للحالات الحالية للمكون الآلي - "a0" ، "b0" ، "c0" ، يتم تحديد انتقالات متغيرات متغيرات الإدخال ^ x1 ^ x2 ، ^ x1x2 ، x1 ^ x2 ، x1x2. نحصل على التحولات على التوالي في تنص على "a0b0c0" و "a0b1c0" و "a1b0c0" و "a1b1c0" والتي تحمل علامة "000" و "010" و "100" و "110" على الجهاز الناتج. يجب عليك تكرار هذه العملية بالتسلسل لجميع الحالات التي يمكن الوصول إليها. حلقاتالتي لا يتم تحميلها بالإجراءات يمكن استبعادها من الرسم البياني.

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

وهو أمر مثير للقلق ، على الرغم من أن الاختبار لم يكشف عن أخطاء. على الرسم البياني للآلة الناتجة ، تم العثور على التحولات المتضاربة في إجراءات الإخراج. يتم تمييزها بمزيج من الإجراءات y1y3 و y2y3. يتم تشغيل الإجراءين y1 و y2 عندما تتغير بيانات الإدخال ، ثم يحسب الإجراء الآخر y3 مجموع المتغيرات بالتوازي معها. ما هي القيم التي ستعمل عليها - القديمة أو التي تغيرتها فقط بقيم جديدة؟ للقضاء على الغموض ، يمكنك ببساطة تغيير إجراءات y3 و y4. في هذه الحالة ، سيكون الرمز الخاص بهم كما يلي: X3 = X1Sav + X2Sav و print (X1Sav، X2Sav، X3).

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

4 - نتائج


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

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

ولكن نعود إلى البرمجة التفاعلية. هل يعتبر RP جميع بيانات البرنامج موازية في البداية؟ يمكننا فقط أن نفترض أنه بدون هذا من الصعب التحدث عن نموذج برمجة "موجه نحو تدفقات البيانات ونشر التغييرات" (انظر تعريف البرمجة التفاعلية في [3]). ولكن ما الفرق بين البرمجة والتحكم في التدفق (لمزيد من التفاصيل ، انظر [1])؟ إذن نعود إلى حيث بدأنا: كيفية تصنيف البرمجة التفاعلية في إطار التصنيفات المعروفة؟ وإذا كان RP شيئًا خاصًا بالبرمجة ، فما الذي يختلف عنه عن نماذج البرمجة المعروفة؟

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

نعم ، لقد نسيت تقريبا. سأصنف البرمجة التلقائية (AP) على أنها برمجة ذات تحكم ديناميكي. أما بالنسبة لعدم التزامن - أراهن. بالنظر إلى أن أساس نموذج التحكم AP هو شبكة في وقت واحد ، أي شبكات متزامنة من الآلي ، ثم متزامن. ولكن نظرًا لأن بيئة VKPa تنفذ أيضًا العديد من الشبكات من خلال مفهوم "عوالم الأوتوماتيكية" ، فهي غير متزامنة تمامًا. بشكل عام ، أنا ضد أي إطار تصنيف صارم للغاية ، ولكن ليس للفوضى. بهذا المعنى ، في VKPa ، آمل أن يتم التوصل إلى حل وسط بين صلابة البرمجة المتوازية التسلسلية وبين الأناركية غير المتزامنة. وبالنظر إلى حقيقة أن البرمجة التلقائية تغطي أيضًا فئة برامج الأحداث (انظر [4]) ، وأن برامج الدفق يتم تصميمها بسهولة داخلها ،ما هي البرمجة التي لا تزال تحلم بها؟ بالتأكيد - بالنسبة لي.

المؤلفات
1. /.. , .. , .. , .. ; . .. . – .: , 1983. – 240.
2. . [ ], : habr.com/ru/post/486632 . . . ( 07.02.2020).
3. . . [ ], : ru.wikipedia.org/wiki/_ . . . ( 07.02.2020).
4. — ? [ ], : habr.com/ru/post/483610 . . . ( 07.02.2020).

Source: https://habr.com/ru/post/undefined/


All Articles