View Full Version : اعمال چند نخی برای کد؟
one hacker alone
شنبه 30 اردیبهشت 1391, 14:43 عصر
سلام دوستان
کد زیر رو در نظر بگیرید
main()
{
f1();
f2();
}
من می خوام دوتا تابع با هم کار کنن حالا چجوری اینهارو چند نخی کنیم
shahmohammadi
شنبه 30 اردیبهشت 1391, 21:05 عصر
سلام.
خواستم فقط توضيح اين كار نباشه و يكم كاربردي تر بشه اومدم برنامه اي نوشتم كه با استفاده از يك تابع مجموع اعداد زوج از يك تا 1000 و در يك تابع مجموع اعداد فرد از يك تا 1000 رو محاسبه مي كنم. هر دو همزمان اتفاق مي افتند. بعدش مي آد مجموعشون رو در خروجي چاپ مي كنه. و اين مي شه مجموع اعداد از يك تا هزار:
//Calculatin sum of 1 to 1000 with 2 threads
#include <stdio.h>
#include <windows.h>
#include <conio.h>
DWORD WINAPI func1();
DWORD WINAPI func2();
unsigned long int sum,sum1,sum2,flg1,flg2;
int main()
{
flg1=flg2=1;
HANDLE Thread1=CreateThread(NULL,0,func1,NULL,0,0);
HANDLE Thread2=CreateThread(NULL,0,func2,NULL,0,0);
while(flg1||flg2);
CloseHandle(Thread1);
CloseHandle(Thread2);
sum=sum1+sum2;
printf("%d ",sum);
getch();
return 0;
}
DWORD WINAPI func1()
{
int i;
for(i=1;i<=1000;i+=2)
sum1+=i;
flg1=0;
}
DWORD WINAPI func2()
{
int i;
for(i=2;i<=1000;i+=2)
sum2+=i;
flg2=0;
}
فقط به ياد داشته باشيد كه همگام سازي در اين مورد خيلي مهم هست. مثلا حلقه اي كه من گذاشتم باعث مي شه كه وقتي يكيش زود تر تموم كرد منتظر اون يكي بمونه.
اگه باز هم مشكلي داشتيد در خدمتيم.
one hacker alone
شنبه 30 اردیبهشت 1391, 23:53 عصر
دوست عزیز ممنون من این رو تو MSVC++ تست کردم ظاهرا شما از کامپایلر دیگه ای استفاده میکنید برای من خطا میده و میگه پارامتر سوم رو نمیتونه تبدیل کنه (منظورم 2 تا نخی هست که ساختین با تابع create)
سوال دیگه اینکه کدوم یک از قسمت های برنامه نوشتنش اجباری هست چون من نخ کار نکردم الان نمیدونم کجای برنامه چکار میکنه
قسمت های زیر چکار میکنن
DWORD WINAPI func1();
DWORD WINAPI func2();
و نکته دیگه اینکه اینجا ما تابع داریم حالا اگه ما در برنامه خود تابع نداشته باشیم و قسمتی از کد که مثلا یه حلقه هست رو بخوایم نخ بدیم چکار کنیم
shahmohammadi
یک شنبه 31 اردیبهشت 1391, 01:01 صبح
توي dev cpp اجراش كردم.
يه توضيح مختصري مي دم.
در قدم اول بايد نخ رو به صورت يه تابع اعلان كنيم:(با هر نامي كه خواستيم)
DWORD WINAPI func1();
من در كد بالا دو تا نخ تعريف كردم.
بعد مي آييم در پايين تر از تابع مين تعريفش مي كنيم. هر چيزي مي تونيم بنويسيم. در بالا حلقهاي بود كه sum1 رو حساب مي كرد. اينجا كاري مي كنيم كه بياد a رو به تعداد زياد چاپ كنه. نيازي هم نيست كه براش شرط خروج بزاريم:
DWORD WINAPI func1()
{
while(1)
{
printf("a");
}
}
الان وقتش رسيده كه بيايم كاري كنيم كه نخمون در برنامه در جايي كه مي خواهيم اجرا شه. براي اين كار از تابع API زير استفاده مي كنيم:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD cbStack,
LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpvThreadParam,
DWORD fdwCreate,
LPDWORD lpIDThread
);
براي ايجاد نخ كافيه كه عبارت زير رو بنويسيم تا از اون سطر به بعد تخ همزمان با برنامه اجرا شه:
HANDLE Thread1=CreateThread(NULL,0,func1,NULL,0,0);
حالا براي اينكه نخ تموم شه از تابع زير استفاده مي كنيم:
CloseHandle(Thread1);
يه برنامه سادهتر:
#include <stdio.h>
#include <windows.h>
#include <conio.h>
DWORD WINAPI func1();
int main()
{
HANDLE Thread1=CreateThread(NULL,0,func1,NULL,0,0);
Sleep(10);
printf(" type with keyboard ");
getchar();
CloseHandle(Thread1);
return 0;
}
DWORD WINAPI func1()
{
while(1)
{
printf("a");
sleep(500);
}
}
در اين برنامه همزمان با اينكه نخ aها رو چاپ مي كنه شما مي تونيد تايپ كنيد.
براي سوال آخرتون فكرنكنم در اينجا بتونيم اين كارو بكنيم. يعني حتما بايد يه تابع تعريف كنيم.
shahmohammadi
یک شنبه 31 اردیبهشت 1391, 01:18 صبح
براي تابع نخ اينو امتحان كنيد ببينيد كامپايرتون اجراش مي كنه؟ به نحوهي دادن آدرس تابع به تابع createthread اشكال گرفته.
DWORD WINAPI func1(LPVOID LpData)
اگه جواب نداد شايد اين جواب بده: داخل createthread نام تابع نخ رو با تبديل صريح بنويسيد:
(LPTHREAD_START_ROUTINE)func1
اين طور كه من فهميدم كامپايلرتون به نوع اين پارامتر اشكال گرفته پس تبديل صريح مي كنيم به نوعي كه انتظار داره. مثل بالا.
one hacker alone
یک شنبه 31 اردیبهشت 1391, 01:31 صبح
من کد شما رو از روی یه کد دیگه تغییر دادم و در نهایت اجرا شد گیرش همLPVOID بود اما قضیه چیه نمیدونم
جالب اینجاست که خروجی برای من 500500 میشه هرکاری کردم همین شد
// thread.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <conio.h>
DWORD WINAPI func1(LPVOID lpParam);
DWORD WINAPI func2(LPVOID lpParam);
unsigned long int sum,sum1,sum2,flg1,flg2;
int main(int argc, char* argv[])
{
flg1=1;
flg2=1;
sum,sum1,sum2=0;
HANDLE Thread1=CreateThread(NULL,0,func1,NULL,0,0);
HANDLE Thread2=CreateThread(NULL,0,func2,NULL,0,0);
while(1)
{
if(flg1==0 && flg2==0)
break;
}
CloseHandle(Thread1);
CloseHandle(Thread2);
sum=sum1+sum2;
printf("%d ",sum);
getch();
return 0;
}
DWORD WINAPI func1(LPVOID lpParam)
{
int i;
if (flg1==0)
return 0;
for(i=1;i<=1000;i+=2)
sum1+=i;
flg1=0;
return 0;
}
DWORD WINAPI func2(LPVOID lpParam)
{
int i;
if (flg2==0)
return 0;
for(i=2;i<=1000;i+=2)
sum2+=i;
flg2=0;
return 0;
}
shahmohammadi
یک شنبه 31 اردیبهشت 1391, 01:53 صبح
500500 جواب درسته.
one hacker alone
یک شنبه 31 اردیبهشت 1391, 13:00 عصر
ما گفتیم sum1,2 رو جمع کن یعنی باید 1000 چاپ میکرد
soorena
یک شنبه 31 اردیبهشت 1391, 13:41 عصر
سلام
مجموعه ۲ تا سری از عددهای زوج و فرد رو داره چاپ میکنه که فکر میکنم همون ۵۰۰۵۰۰ بشه.
mehdi.mousavi
یک شنبه 31 اردیبهشت 1391, 14:46 عصر
سلام دوستان کد زیر رو در نظر بگیرید من می خوام دوتا تابع با هم کار کنن حالا چجوری اینهارو چند نخی کنیم
سلام.
انواع و اقسام متودها و تکنولوژی ها رو برای اینکار در Windows (یا دیگر سیستم عامل ها) داریم، که برخی (بسته به Compiler) دارای ایراداتی است که میتونه منجر به Memory Leak بشه و برخی خیر. من فرض میکنم شما از VS2010 استفاده می کنید که دیگه وارد این بحث نشم که چرا نباید در CVC6.0 از CreateThread استفاده کرد و _beginthreadex اونجا ارجحیت داره. در هر حال، کدی که دوستمون نوشتن ایراد بسیار بسیار بزرگی داره (فارغ از عدم چک کردن Return Value توابع...) و اون ایراد این هستش که از دو flag برای متوجه شدن از اتمام کار Thread استفاده کرده اند. اما این چرا ایراد محسوب میشه؟ وقتی شما اینکارو کنید، اونوقت مجبورید جایی از کد، حلقه ای بذارید (while true، while 1 و ...) که بی نهایت بار باید بچرخه تا ببینه آیا وظیفه کلیه thread ها انجام شده یا خیر و این حلقه، براحتی CPU Usage سیستم رو به 100% میرسونه. روش صحیح برای کنترل همزمانی Thread ها، استفاده از Synchronization Object هاست (لطفا روی گوگل جستجو کنید، فرصا ندارم تا هر کدوم و تفاوت هر یک رو با دیگری براتون توضیح بدم). بدین ترتیب، شما از سیستم میخواهید تا وقتی کار کلیه Thread ها تموم نشده، ادامه اجرای Main Thread به تعویق بیفته، بدون اینکه نیازی به چک کردن Flag ای داشته باشید. من کد مزبور رو تغییر دادم تا سریعتر متوجه موضوع بشید، اما لطفا کد رو Copy & Paste نکنید و ابتدا با Thread و مفاهیم مرتبط با اون، به خوبی آشنا بشید:
#include "stdafx.h"
#include "conio.h"
#include "process.h"
#include "windows.h"
DWORD dwSum1, dwSum2;
DWORD WINAPI func1(LPVOID lpParam)
{
for(int i = 1; i <= 1000; i += 2)
dwSum1 += i;
return 0;
}
DWORD WINAPI func2(LPVOID lpParam)
{
for(int i = 1; i <= 1000; i += 2)
dwSum2 += i;
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hThreads[2] = { NULL };
dwSum1 = dwSum2 = 0;
hThreads[0] = CreateThread(NULL, 0, func1, NULL, 0, 0);
if(hThreads[0] == NULL)
return -1;
hThreads[1] = CreateThread(NULL, 0, func2, NULL, 0, 0);
if(hThreads[1] == NULL)
{
CloseHandle(hThreads[0]);
return -1;
}
WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
CloseHandle(hThreads[0]);
CloseHandle(hThreads[1]);
return 0;
}
WaitForMultipleObjects در واقع API ای هستش که منتظر میمونه کلیه thread هایی که بهش معرفی میکنم، به وضعیت Signaled برن. به بیان دیگه، تا وقتی اجرای دو تابع func1 و func2 تموم نشده باشه، اجرا در خط WaitForMultipleObjects متوقف خواهد شد (توقفی که پردازشی در اون صورت نمیگیره).
موفق باشید.
pe32_64
یک شنبه 31 اردیبهشت 1391, 17:41 عصر
پیرو ی مطالب استاد اضافه کنم که در سایتcodeproject.com مطالب بسیار خوبی برای شما موجود است.
one hacker alone
دوشنبه 01 خرداد 1391, 02:39 صبح
اگه پارامتر سوم تابع WaitForMultipleObjects مقدار false بگیره چی میشه
mehdi.mousavi
دوشنبه 01 خرداد 1391, 10:43 صبح
اگه پارامتر سوم تابع WaitForMultipleObjects مقدار false بگیره چی میشه
در این صورت کار اولین Thread ای که به پایان برسه، از Wait خارج میشه و Main Thread ادامه پیدا میکنه (و دیگه منتظر اتمام کار مابقی thread ها نخواهد موند).
one hacker alone
پنج شنبه 18 خرداد 1391, 01:30 صبح
ببخشید که من مطلب رو دوباره به جریان انداختم اونم دلیلش این بود که چرا ما برای پیش الگوی تابع از نوع DWORD استفاده کردیم و اینکه چرا برای ورودی تابع از یک اشاره گر استفاده کردیم؟
شاید تابع من ورودی int داشته باشه و خروجی هم مثلا char باشه اونوقت تکلیف چیه؟
mehdi.mousavi
پنج شنبه 18 خرداد 1391, 18:09 عصر
چرا ما برای پیش الگوی تابع از نوع DWORD استفاده کردیم و اینکه چرا برای ورودی تابع از یک اشاره گر استفاده کردیم؟ شاید تابع من ورودی int داشته باشه و خروجی هم مثلا char باشه اونوقت تکلیف چیه؟
سلام.
این تابع CreateThread هستش که داره اون Signature رو برای تابع مورد نظر به ما دیکته می کنه. مقدار خروجی تابع که یک DWORD هستش، نباید برای نیتی جز "موفقیت / عدم موفقیت آمیز" بودن کار تابع استفاده بشه. همونطوریکه مشاهده کردید، من هم می تونستم dwSum1 رو بعنوان خروجی برگردونم و ... اما این درست نبود (اگر چه امکان پذیره) و این کارو انجام ندادم (و از متغیر dwSum1 استفاده کردم). اما اگر اینکارو می کردم، اونوقت باید با API مربوطه Exit Code اون Thread رو میگرفتم (که نشوندهنده همون DWORD بازگشتی تابع بودش) و از اون در ادامه کد استفاده میکردم... شما هم اگر خیلی اصرار دارید که یک CHAR رو برگردونید، مشکلی نخواهید داشت، چون Char Data Type از DWORD Data Type کوچکتره و در نتیجه میتونید با Cast کردن Thread Exit Code به Char مورد نظر برسید.
اما در مورد پارامتر ورودی تابع، یعنی داده ای از نوع LPVOID - یا Long pointer to a void object. این چی میخواد بگه؟ این میگه که پارامتر ورودی تابع، اشاره گری است به هر Object ای که مایل بودیم و هیچ محدودیتی وجود نداره. اون Object میتونه یک INT ساده یا یک Structure پیچیده باشه و ... Compiler شلوغ نکن و بذار برنامه نویس هرکاری مایل هستش با این اشاره گر بکنه. بدین ترتیب، این امکان برای ما پیش میاد که Object ای از هر نوع که مایل بودیم رو بعنوان ورودی به تابع Thread امون بدیم، اما وظیفه Track کردن اون Object دیگه بر عهده ماست... پس، در پاسخ به این سوال که "شاید تابع من ورودی INT داشته باشه" میگم: "اشکالی نداره، INT رو هم می تونید بشکل یک LPVOID به اون تابع پاس کنید".
موفق باشید.
ali chegini
پنج شنبه 25 مهر 1392, 21:44 عصر
سلام.
اگر خواستید کاملا این مسائل رو متوجه بشید و بتونید از سمافور ها یا مانیتور ها و ویژگی های این موارد در چند نخی استفاده کنید و موارد دیگه کتاب : سیستم های عامل با رویکرد حل مساله . مولف : دکتر ابوالفضل طرقی حقیقت رو بخونید.
خیلی کمک میکنه.
vasilopita
شنبه 27 مهر 1392, 11:08 صبح
سلام. چون دیدم سوالم با موضوع این تاپیک نزدیکه منم جسارتا اینجامطرح می کنم :
من می خوام thread ام رو با تابع suspenthread به حالت تعویق دربیارم. این تابع نیاز به هندل thread داره. من توسط تابع beginthreadex_ ترد رو ساختم. سعی کردم با getcurrentthread هندلش رو بدست بیارم ولی به نظر نشد. لطفا راهنمایی کنید.ممنون
vasilopita
شنبه 27 مهر 1392, 11:24 صبح
سلام. چون دیدم سوالم با موضوع این تاپیک نزدیکه منم جسارتا اینجامطرح می کنم :
من می خوام thread ام رو با تابع suspenthread به حالت تعویق دربیارم. این تابع نیاز به هندل thread داره. من توسط تابع beginthreadex_ ترد رو ساختم. سعی کردم با getcurrentthread هندلش رو بدست بیارم ولی به نظر نشد. لطفا راهنمایی کنید.ممنون
پیدا کردم خودم:
HANDLE hndl=(HANDLE)_beginthreadex(NULL,0,func,&i,0,NULL);
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.