לדלג לתוכן

פתרון תהליכונים והקשר ביצוע

שני העובדים מקבלים TLS נפרד, ולכן local_calls מתחיל מאפס בכל אחד מהם. שתי קריאות באותו thread משתמשות באותו עותק ומקדמות אותו לאחד ואז לשניים. הפרדה זו אינה קשורה לכתובות של איברי work, שנמצאים באותו מערך משותף.

בכשל מדומה אחרי העובד הראשון, המשתנה started נשאר אחד והלולאה ממתינה ל־handle היחיד שנרכש. רק אחרי ההמתנה מותר להשמיד את work. אסור לסגור handle ולפרש זאת כאילו התבצע join.

מימוש C++20 חלופי יכול להשתמש בקוד הבא. הוא מחליף את main ומשתמש ב־Work וב־worker מההרצאה; מוסיפים את header של thread.

#include <thread>

int main() {
    std::array<Work, 2> work{{{17}, {29}}};
    {
        std::jthread first([&] { worker(&work[0]); });
        std::jthread second([&] { worker(&work[1]); });
    }
    for (const auto& item : work)
        std::printf("result=%llu tls=%u\n",
            static_cast<unsigned long long>(item.result), item.tls_value);
}

ה־scope הפנימי מסתיים לפני קריאת התוצאות, ולכן destructors של jthread מבצעים join. אם יצירת second זורקת חריגה, first עדיין מושמד וממתין, בעוד work נשאר חי כי נבנה לפניו.

ה־jthread מבקש stop בעת ההשמדה, אך הקוד שלנו אינו בודק stop token. לכן ההשמדה ממתינה לסיום הלולאה הרגילה. עצם השימוש בסוג אינו מוסיף ביטול שיתופי לאלגוריתם שלא תוכנן לכך.

ב־trace מחפשים מקטעי running של שני ה־thread IDs על שני מעבדים לוגיים שונים בזמן חופף. הופעת שני IDs באותו log אינה מספיקה. גם מספרי CPU שנדגמו ברגעים שונים אינם מוכיחים חפיפה.

הסבר הזיכרון צריך להראות שהמחסניות נפרדות, אך מצביע מהמחסנית של כל עובד יכול להגיע למערך work של ההורה. בידוד stack אינו בידוד כל הזיכרון של ה־thread.