ورود

View Full Version : توابع Thread safe



ayub_coder
جمعه 04 بهمن 1392, 12:26 عصر
با سلام
وقتی میگویند که تابعی یا کدی Thread safe هست یا نه یعنی چه معنی میده؟
منظور از thread-safe چیه؟ و چه کدی thread safe هست و جچه کدی نیست
ممنون

کامبیز اسدزاده
جمعه 04 بهمن 1392, 14:44 عصر
این مربوط به تابع نویسی هستش و ربطی به MFC بودن یا نبودن نداره.
ولی بزارید یه توضیحی بدم تا بهتر متوجه موضوع بشید.


//Thread-safe
double add_one(double d)
{
return d + 1;
}

struct ME
{
double m_data;

//Not thread-safe
double
run(double d)
{
m_data += d;
return m_data / 2.0;
}
}


ببینید در کد بالا دو تا تابع تعریف کردیم که یکیش thread-safe هستش و دومیش خیر !
منظور از این واژه از معنیش هم مشخصه (رشته های امن) که در کل صدق میکنه به امن بودن تابع شما.

در تابع اول ما یه تابع تعریف کردیم به نام add_one که چیکار میکنه مقدار رو میگیره و در نهایت مقدار بازگشتی رو از بدنه خارج و به خروجی میده این نوع تابع در هر زمان از پردازش میتونه یک مقدار رو بگیره و نتیجه رو به خروجی بده و این میتونه همیشه و همیشه ادامه پیدا کنه بدونه اینکه مشکلی بوجود بیاد.

حالا در گزینه دوم یه ساختار تعریف کردیم که شامل یک بدنه هستش و مثل تابع بالا کاری که میخوایم رو انجام میده ولی از لحاظ ساختاری دارای یک الگوی درست حسابی نیست منظورم همین الگوی تابع هست که ورودی رو میگیره در بدنده پردازش میکنه و در نهایت خروجی میده و شما تا زمانی که ورودی ندین به تابع بدنه تابع تغییر و عملیاتی رو انجام نمیده ولی در مورد دوم قضیه فرق داره امکان داره شما وسط کار توی کلاس یا ساختار چند سطر اینور اونورتر مقدار m_data رو تغییر بدین یا به هر نحوی یجوری تاثیر بزارید روی این متغیر که باعث میشه کلا فرایند کار نوع دوم تحت تاثیر قرار بگیره در کل در حالت دوم دلو روده ساختار بیرونه و میشه راحت تحت تاثیر قرار بگیره و نتیجه اون ممکنه با نتیجه تابع safe شده در موقع اجرا متفاوت باشه.


برای همین گزینه اول که شامل یه ساختار درست حسابی هستش مطمئن و امن هست ولی گزینه دوم خیر چنین نیست.

ayub_coder
جمعه 04 بهمن 1392, 15:05 عصر
چون دومی به صورت structure هست این مشکل رو داره؟

کامبیز اسدزاده
جمعه 04 بهمن 1392, 15:50 عصر
چون دومی به صورت structure هست این مشکل رو داره؟
مهم structure بودن نیست !

شما خارج از تابعش متغیر رو دارید به صورت double m_data; که میتونه قبل از ورود به تابع مقدار بگیره و موجب تغییر در نتیجه بشه.

شما فرض کنید یه کدی مینویسید که یه خروجی رو بده بیرون ولی این کد دارای یه بدنه درست حسابی نباشه اونوقت میشه بهش گفت Unsafe ! شما باید به این توجه کنید که تولید کننده خروجی مورد نظر شما اگر به صورت یک بدنه ورودی / پردازش / خروجی Safe نباشه احتمال این هست که در زمان اجرا توسط دیگر توابع یا Call های که به طرف مقادیر میره تحت تاثیر قرار بگیره.

ساده تر بگم شما در کد دوم میبینید که اصلا به صورت یک بدنه کامل کد نوشته نشده و کاملا باز هست و میتونه توسط هر نوع Call و تعاریف دوباره تغییر پیدا کنه در حالی که در حالت اول تا زمانی که شما از تابع رو صدا نزنی هیچ ورودی رو دریافت نمیکنه که به خروجی بده ولی در نمونه دوم اینطور نیست.

ayub_coder
جمعه 04 بهمن 1392, 16:16 عصر
پس باید متغییر های مورد اسفاده محلی و private باشن تا از بیرون نتونن تغییر کنن و فقط از داخل تابع تغییر پیدا کنن درسته؟

کامبیز اسدزاده
جمعه 04 بهمن 1392, 16:26 عصر
پس باید متغییر های مورد اسفاده محلی و private باشن تا از بیرون نتونن تغییر کنن و فقط از داخل تابع تغییر پیدا کنن درسته؟
اینکه محلی باشند درست ولی در همون محل خودشون هم میتونن تغییر پیدا کنن و توسط دیگر توابع Unsafe موجود در کلاس تحت تاثیر قرار بگیرند پس بهترین روش برای یک عمل تابعی رعایت استاندارد تابع نویسی هستش و این روش در کد های کوچیک خیلی مهم نیست ولی در پروژه هایی که هسته اونا خیلی پیچیده هست و توابع و کلاس های تو در تو دائما در حال عملیات در هر زمانی هستند بهتره Safe نویسی رو رعایت کنیم.

ayub_coder
جمعه 04 بهمن 1392, 16:32 عصر
رعایت استاندارد تابع نویسی هستش


میشه توضیح بدی و مثال بزنی تا بهتر متوجه شم.

کامبیز اسدزاده
جمعه 04 بهمن 1392, 16:40 عصر
رعایت استاندارد تابع نویسی هستش


میشه توضیح بدی و مثال بزنی تا بهتر متوجه شم.

اینکه مثال زدن نداره ! و تنها مربوط به C++‎ نیست !
شما باید برای کدهایی که عملیات خاصی انجام میدن تابع نویسی کنید و به صورت ساده...

مثلا کد زیر رو در نظر بگیرید :

به نظرت این مطمئنه ...


using namespace std;

int main()
{
int a , b;
int c;
c = a + b;
cout << c;
getchar();
}



یا این یکی :


using namespace std;

int myvoid(int a , int b)
{
int c;
c = a + b;

return c;
}

int main()
{
myvoid(100,500);
}


مسلما حالت دوم در کد نویسی قابل اعتماد تره و ترو تمیز تر و هر موقع که لازم باشه ازش استفاده میشه در مورد خود توابع و رابطه اونها با کلاس ها همینطوره یعنی شما کد هاتون رو تا جایی که میتونید باید بهینه بنویسید.

Vitallity
جمعه 04 بهمن 1392, 17:19 عصر
why.darkness مثال درستی زدند، اما خیلی بد توضیح دادن :دی
زمانی یه تابع برای Multithreading ایمن نیست که از منابع مشترک استفاده بشه... مثلاً شما یک متغیر داری که تعداد اشیای مشخصی رو ذخیره می‌کنه و حالا شما از دو یا چند Thread جداگانه در برنامه استفاده می‌کنی که تعداد این اشیا رو شمارش کنند. به این توجه داشته باشید این Thread ها که تابع مشخصی رو دارن اجرا می‌کنن از نتیجه هم آگاه نیستند و به صورت موازی دارن کاری رو انجام می‌دن... اتفاقی که میفته نتیجه کاملاً اشتباهی هست.
این نمونه کد رو اجرا کن:

void count(int& i)
{

for(int x = 0; x < 3; x++)
{
i += 1;
std::cout << i << std::endl;
}
}

int main()
{
int a = 0;
std::thread t1(count, a);
std::thread t2(count, a);


t1.join();
t2.join();

std::cout << a << std::endl;
std::cin.get();
return 0;
}
نتیجه قابل انتظار باید 1 2 3 4 5 6 باشه در صورتی که 1 1 2 3 3 2 هست. به این حالت Race condition گفته می‌شود. این تابع Thread-safe نیست. برای ایمن کردن این تابع باید هر دو thread رو synch کنیم. برای مطالعه پیشنهاد می‌کنم کتاب C++ Concurrency in Action مطالعه کنید.