با استفاده از کتابخانه pthread، می توانید مدیریت رشته های سطح پایین را برای برنامه نویسی با کارایی بالا انجام دهید.
در لینوکس، میتوانید رشتهها را در C/C++ با استفاده از کتابخانه POSIX thread (pthread) ایجاد و مدیریت کنید. برخلاف سایر سیستم عامل ها، تفاوت کمی بین یک Thread و یک Process در لینوکس وجود دارد. به همین دلیل است که لینوکس اغلب از موضوعات خود به عنوان فرآیندهای سبک وزن یاد می کند.
با استفاده از کتابخانه pthread، میتوانید رشتههایی ایجاد کنید، منتظر بمانید تا خاتمه پیدا کنند و صریحاً آنها را خاتمه دهید.
تاریخچه استفاده از Thread در لینوکس
قبل از نسخه 2.6 لینوکس، اجرای رشته اصلی LinuxThreads بود. این پیاده سازی از نظر عملکرد و عملیات همگام سازی محدودیت های قابل توجهی داشت. محدودیت در حداکثر تعداد رشتههایی که میتوانست اجرا شوند، آنها را در 1000 محدود میکرد.
در سال 2003، تیمی به رهبری توسعه دهندگان IBM و RedHat موفق شدند پروژه NPTL (Native POSIX Thread Library) را در دسترس قرار دهند. اولین بار در RedHat Enterprise نسخه 3 برای حل مشکلات عملکرد ماشین مجازی جاوا در لینوکس معرفی شد. امروزه، کتابخانه گنو C شامل پیادهسازی هر دو مکانیزم رشتهسازی است.
هیچکدام از اینها پیادهسازی رشتههای سبز نیستند، که یک ماشین مجازی آنها را در حالت صرفاً کاربر مدیریت و اجرا میکند. هنگامی که از کتابخانه pthread استفاده می کنید، هسته هر بار که یک برنامه شروع می شود یک رشته ایجاد می کند.
میتوانید اطلاعات مربوط به رشته را برای هر فرآیند در حال اجرا در فایلهای زیر /proc/
منطق کاری موضوعات
Thread ها مانند فرآیندهایی هستند که در حال حاضر روی سیستم عامل اجرا می شوند. در سیستم های تک پردازنده (مانند میکروکنترلرها)، هسته سیستم عامل رشته ها را شبیه سازی می کند. این اجازه می دهد تا تراکنش ها به طور همزمان از طریق برش اجرا شوند.
یک سیستم عامل تک هسته ای واقعاً می تواند در هر زمان فقط یک فرآیند را اجرا کند. با این حال، در سیستم های چند هسته ای یا چند پردازنده ای، این فرآیندها می توانند به طور همزمان اجرا شوند.
ایجاد موضوع در C
می توانید از تابع pthread_create برای ایجاد یک رشته جدید استفاده کنید. فایل هدر pthread.h شامل تعریف امضای آن به همراه سایر توابع مرتبط با رشته است. Thread ها از فضای آدرس و توصیفگرهای فایل مشابه برنامه اصلی استفاده می کنند.
کتابخانه pthread همچنین شامل پشتیبانی لازم برای عملیات mutex و شرطی مورد نیاز برای عملیات همگام سازی است.
هنگامی که از توابع کتابخانه pthread استفاده می کنید، باید مطمئن شوید که کامپایلر کتابخانه pthread را به فایل اجرایی شما پیوند می دهد. در صورت لزوم، می توانید به کامپایلر دستور دهید تا با استفاده از گزینه -l به کتابخانه پیوند دهد:
gcc -o test test_thread.c -lpthread
تابع pthread_create دارای امضای زیر است:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
در صورت موفقیت آمیز بودن رویه، 0 را برمی گرداند. در صورت وجود مشکل، کد خطای غیر صفر را برمی گرداند. در امضای تابع بالا:
- پارامتر thread از نوع pthread_t است. موضوع ایجاد شده همیشه با این مرجع قابل دسترسی خواهد بود.
- پارامتر attr به شما امکان می دهد رفتار سفارشی را مشخص کنید. برای تنظیم این مقدار می توانید از یک سری توابع ویژه رشته استفاده کنید که با pthread_attr_ شروع می شوند. سفارشیسازیهای ممکن عبارتند از: خطمشی زمانبندی، اندازه پشته و خطمشی جداسازی.
- start_routine تابعی را که thread اجرا خواهد شد مشخص می کند.
- arg یک ساختار داده عمومی را نشان می دهد که توسط نخ به تابع ارسال می شود.
در اینجا یک نمونه برنامه وجود دارد:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void *worker(void *data)
{
char *name = (char*)data;
for (int i = 0; i < 120; i++)
{
usleep(50000);
printf("Hi from thread name = %s\n", name);
}
printf("Thread %s done!\n", name);
return NULL;
}
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("Exiting from main program\n");
return 0;
}
انواع موضوع
وقتی یک thread از تابع main() در یک برنامه باز می گردد، همه رشته ها خاتمه می یابند و سیستم تمام منابع مورد استفاده برنامه را آزاد می کند. به همین ترتیب، هنگام خروج از هر رشته با دستوری مانند exit()، برنامه شما تمام رشته ها را خاتمه می دهد.
با تابع pthread_join، می توانید منتظر بمانید تا یک رشته به جای آن خاتمه یابد. رشته ای که از این تابع استفاده می کند تا زمانی که رشته مورد انتظار خاتمه یابد مسدود می شود. منابعی که از سیستم استفاده می کنند حتی در مواردی مانند خاتمه رشته های قابل اتصال، برنامه ریزی نشده توسط CPU یا حتی عدم اتصال با ptread_join باز نمی گردند.
گاهی اوقات شرایطی وجود دارد که پیوستن به pthread_join معنی ندارد. مثلاً اگر پیشبینی زمان پایان موضوع غیرممکن باشد. در این حالت، می توانید اطمینان حاصل کنید که سیستم تمام منابع را به طور خودکار در نقطه ای که رشته برمی گرداند، برمی گرداند.
برای رسیدن به این هدف، باید موضوعات مربوطه را با وضعیت DETACHED شروع کنید. هنگام شروع یک رشته، وضعیت DETACH را می توان از طریق مقادیر ویژگی thread یا با تابع pthread_detach تنظیم کرد:
int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int pthread_detach(pthread_t thread);
در اینجا نمونه ای از استفاده از pthread_join (). تابع اصلی را در برنامه اول با موارد زیر جایگزین کنید:
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("exiting from main program\n");
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}
هنگامی که برنامه را کامپایل و اجرا می کنید، خروجی شما به صورت زیر خواهد بود:
Hi from thread Y
Hi from thread X
Hi from thread Y
...
Hi from thread Y
exiting from main program
Hi from thread X
...
Hi from thread X
Thread X done!
Hi from thread Y
Thread Y done!
پایان موضوع
شما می توانید یک موضوع را با فراخوانی به pthread_cancel لغو کنید و شناسه pthread_t مربوطه را ارسال کنید:
int pthread_cancel(pthread_t thread);
می توانید این عمل را در کد زیر مشاهده کنید. باز هم، فقط عملکرد اصلی متفاوت است:
int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(1);
printf("====> Cancelling Thread Y!!\n");
pthread_cancel(th2);
usleep(100000);
printf("====> Cancelling Thread X!\n");
pthread_cancel(th1);
printf("exiting from main program\n");
return 0;
}
چرا تاپیک ها ایجاد می شوند؟
سیستمهای عامل همیشه سعی میکنند رشتهها را روی یک یا چند CPU اجرا کنند، چه از یک لیست خود ایجاد شده یا از یک لیست رشته ایجاد شده توسط کاربر. برخی از رشته ها نمی توانند اجرا شوند زیرا منتظر سیگنال ورودی/خروجی از سخت افزار هستند. آنها همچنین ممکن است داوطلبانه منتظر باشند، منتظر پاسخ از یک رشته دیگر باشند، یا یک رشته دیگر آنها را مسدود کند.
می توانید منابعی را که به رشته هایی که با استفاده از pthread ایجاد می کنید تخصیص می دهید تنظیم کنید. این میتواند یک خطمشی زمانبندی سفارشی باشد یا در صورت تمایل میتوانید الگوریتمهای زمانبندی مانند FIFO یا Round-robin را انتخاب کنید.