PDA

View Full Version : حرفه ای: مشکل با Thread Queue



r0ot$harp
جمعه 21 مرداد 1390, 21:50 عصر
سلام دوستان عزیز .

یه برنامه ای دارم می نویسم که باید یه سری کار رو با Thread انجام بده .

به صورت ساده برنامه کار می کنه . تنها مشکلی که هست من تا حالا زیاد با Thread ها کار نکردم .احساس می کنم روش ایجاد Thread هام اشتباه هست .

اینطوری فرض کنید قبل شروع Thread من 100 تا پروسه دارم که باید انجام بشند .
این پروسه ها هر کدوم شاید تا چند دقیقه زمان صرف کنند .

من این Thread ها رو اینطور ایجاد می کنم :

کد زیر در حلقه ای قرار دارد که هر Step اون با تغییر DeviceInfo همراه هست.


ThreadStart starter = new ThreadStart(delegate { SendFile(DeviceInfo, Send.SingleFileName); });
Thread thread = new Thread(starter);
thread.Start();

SendFile یک تابع هست با دو آرگومان ورودی .

در قدم بعدی برنامه همیشه نمی تونم این Thread هارو اجرا کنه . برای اینکه Thread که ایجاد شده و در زمان اجرا اجازه انجام کار رو نداره اومدم و از Queue استفاده کردم . کد زیر مربوط به انجام این کار هست :

static Queue q = Queue.Synchronized(new Queue());
static Thread Run_Queue = new Thread(RunQueue);

ThreadStart starter = new ThreadStart(delegate { SendFile(DeviceInfo, Send.SingleFileName); });
Thread thread = new Thread(starter);
thread.Start();
q.Enqueue(starter);


static private void RunQueue()
{
for (int i = 0; i <= q.Count; i++)
{
ThreadStart starter = new ThreadStart((ThreadStart)q.Dequeue());
Thread thread = new Thread(starter);
thread.Start();
Thread.Sleep(1000);

}
}

خوب برنامه کار می کنه اما تنها مشکل اینجاست که اکثر Thread ها تکراری هستند .

مثلا Thread 1 حاوی اسم احسان هست .
Thread 2 حاوی اسم احسان .
3 حاوی اسم ایمان .
و ....

اکثرا تکراری هست .

لطفا مشکل برنامه رو بهم بگین .
خودم احساس می کنم مشکل در زمانی هست که Thread ها بدون وقفه میانی ساخته می شند . یا اینکه Thread ها باید به صورت Array ساخته بشند .

باتشکر احسان

gwbasic
شنبه 22 مرداد 1390, 10:16 صبح
مورد سوالتون رو خوب متوجه نشدم ولی اشکالی که به کار شما وارده اینه که تعداد thread ها زیاد هستند چرا اینقدر thread ایجاد می کنید! نمی تونید در یک thread این کار رو انجام بدید چون ایجاد thread هزینه بر هست

محسن شامحمدی
شنبه 22 مرداد 1390, 13:05 عصر
چرا از کلاس Thread Pool (http://msdn.microsoft.com/en-us/library/ms973903.aspx)استفاده نمی کنید؟
این کلاس دقیقا برای چنین مواردی طراحی شده.
یعنی شما کلی ترد رو میریزید توی صف کاری اون کلاس و اون خودش دونه دونه واسه خودش اجرا می کنه.
ضمنا مشکلات اینچنینی مثل تردهای تکراری در این روش 100 درصد منتفیه.
ضمنا اگه خواستید می تونید از آموزش چندنخی که لینکش توی امضام هست استفاده کنید.(توش کار با این کلاس هم آموزش داده شده)

موفق باشید

r0ot$harp
شنبه 22 مرداد 1390, 14:58 عصر
چرا از کلاس Thread Pool (http://msdn.microsoft.com/en-us/library/ms973903.aspx)استفاده نمی کنید؟
این کلاس دقیقا برای چنین مواردی طراحی شده.
یعنی شما کلی ترد رو میریزید توی صف کاری اون کلاس و اون خودش دونه دونه واسه خودش اجرا می کنه.
ضمنا مشکلات اینچنینی مثل تردهای تکراری در این روش 100 درصد منتفیه.
ضمنا اگه خواستید می تونید از آموزش چندنخی که لینکش توی امضام هست استفاده کنید.(توش کار با این کلاس هم آموزش داده شده)

موفق باشید

سلام دوست عزیز .


ممنون از شما . در مورد سوال دوست عزیز gwbasic باید بگم که کاری که می کنم باید Thread های زیادی رو ایجاد کنه و در مواردی وقتی 100 Thread داریم فقط باید 4 Thread اجرا بشه و تا پایان کار این 4 تا بقیه تو صف باشند .

دوست عزیز محسن شامحمدی از شما هم ممنونم . من تاحالا با Thread ها کار نکردم و تجربه ای نداشتم که از اول برم سراغ Thread Pool .


باتشکر احسان

r0ot$harp
شنبه 22 مرداد 1390, 15:17 عصر
فقط یه سوال خیلی مهم .
من قبلا وقتی Thread هارو تو Queue می ریختم میومدم اون Queue رو استارت می کردم . اما ایان Thraed Pool بعد از ایجاد بدون استارت اجرا می شود . می تونم کار کنم که این اتفاق نیوفته و خوده من دستی اون Start یا Stop کنم ؟؟؟؟

احساس می کنم روی این کلاس اصلا نمی شه محدودیت هایی که روی Thread می شد اعمال کرد رو انجام داد .

الان من اگر احتیاج داشته باشم که Sleep میان اجرای هر کدوم بدم باید چی کار کنم ؟؟؟

در ضمن می شه این Thread هارو داخل Queue ریخت و از داخل Queue با Dequeue اجرا کرد ؟؟؟

باتشکر احسان

در ضمن اگر به خوای کد خودم رو بهینه کنم که همزمانی روی هم اتفاق نیوفته باید چی کار کنم ؟؟؟؟

باتشکر احسان

r0ot$harp
شنبه 22 مرداد 1390, 20:56 عصر
جناب محسن شامحمدی عزیز این پروژه برام خیلی حیاتی هست . تحویل دادم اما این قسمت به مشکل خورده . ممنون می شم کمک کنید .

باتشکر احسان

r0ot$harp
یک شنبه 23 مرداد 1390, 15:34 عصر
سلام دوستان عزیز .

مجبور شدم یه آف بزارم تا تاپیک بیاد بالا .

کسی نیست جواب بده ؟؟؟

باتشکر احسان

محسن شامحمدی
یک شنبه 23 مرداد 1390, 16:36 عصر
در ضمن اگر به خوای کد خودم رو بهینه کنم که همزمانی روی هم اتفاق نیوفته باید چی کار کنم ؟؟؟؟

لطفا مشکل برنامه رو بهم بگین .
خودم احساس می کنم مشکل در زمانی هست که Thread ها بدون وقفه میانی ساخته می شند . یا اینکه Thread ها باید به صورت Array ساخته بشند .مشکل برنامه اینه که وقتی یک ترد رو از توی queue در میارید بلافاصله دوباره درخواست می دید که توی این لحظه چون درخواست بصورت تقریبا همزمان ارسال شده queue یک مقدار رو دوبار یا حتی بیشتر به شما می ده.
روش حل: استفاده از قفل (http://vbupload.persiangig.com/Archive/Multi%20Threading%20IN%20Visual%20Basic.Net.zip)ی ا بیشتر کردن فاصله زمانی گرفتن تردها


اما ایان Thraed Pool بعد از ایجاد بدون استارت اجرا می شود؟بله همینطوره.


می تونم کار کنم که این اتفاق نیوفته و خوده من دستی اون Start یا Stop کنم ؟؟؟؟اطلاعی ندارم شاید امکانش باشه.


در مواردی وقتی 100 Thread داریم فقط باید 4 Thread اجرا بشه و تا پایان کار این 4 تا بقیه تو صف باشند .
این کلاس این امکان رو بهتون می ده که maximum تردهایی که همزمان اجرا می شن رو کنترل کنید.


الان من اگر احتیاج داشته باشم که Sleep میان اجرای هر کدوم بدم باید چی کار کنم ؟؟؟توی تابع همون ترد می نویسید
Thread.sleep(n)


در ضمن می شه این Thread هارو داخل Queue ریخت و از داخل Queue با Dequeue اجرا کرد ؟؟؟یعنی همین روشی که الان انجام دادین؟
بازم اگه سوالی بود در خدمتم*/

vcldeveloper
یک شنبه 23 مرداد 1390, 20:51 عصر
مجبور شدم یه آف بزارم تا تاپیک بیاد بالا .
از دیشب تا به حال این تاپیک دوباره جلوی چشمم ظاهر شد. از اونجایی که من برنامه نویس دات نت نیستم، معمولا توی تاپیک های این تالار پستی ارسال نمی کنم.
اما دوست عزیز، توصیه من به شما این هست که وقتی Multi-threading کار می کنید، خیلی خلیی مراقب باشید! Multi-threading چیزی نیست که اگر یک خرده چیزی ازش بدونید، برنامه تون یک خرده خوب بشه، و اگر خیلی ازش بدونید، برنامه تون کارایی اش عالی بشه! در واقع اگر از Multi-threading به اندازه کافی اطلاع نداشته باشید، استفاده از اون باعث میشه که برنامه تون به گند کشیده بشه. اگر بلد باشید و بدونید دارید چیکار می کنید، کارایی برنامه تون خیلی خوب و عالی میشه.
با این توضیح، وقتی شما میگید:

تنها مشکلی که هست من تا حالا زیاد با Thread ها کار نکردم .احساس می کنم روش ایجاد Thread هام اشتباه هست .
و بعدش هم اصرار می کنید که برنامه تون باید 100 Thread هم داشته باشه، من از آخر و عاقبت اون نرم افزاری که دارید می نویسید، و دردسرهایی که باید برای دیباگ کردنش بکشید، می ترسم!

Thread Pool یک کارکرد مشخص داره، اینجوری نیست که هر وقت دلمون خواست یک لیست از Thread ها داشته باشیم، اونها رو بریزیم توی یه Pool. از طرف دیگه، همینطوری که نمیشه Thread اضافه کرد؛ ساخت Thread خودش سربار بالایی داره، و از طرف دیگه، تعداد بالای Thread ها به اون معنا نیست که کارهای شما هم به همون تعداد Thread ایی که ساختید همزمان میشند؛ مثلا در یک سیستم 4 هسته ایی، یک Thread که داره روی یک هسته کار مدیریت رابط کاربر گرافیکی شما را انجام میده، سایر Thread هایی که ایجاد می کنید، باید روی همون هسته یا 3 هسته دیگه در داخل صف اجرا قرار بگیرند. حالا اگر بر فرض شما 100 عدد Thread درست کنید، این چیزی جز سر بار و شلوغ کردن اون صف ها روی این 4 هسته نیست.

الان مشخص نیست که شما دقیقا میخواید چیکار کنید؛ توصیه من به شما این هست که از مفهوم Thread بیاید بیرون؛ برای خودتون تحلیل کنید که چه کاری (Task) قرار هست انجام بشه، این کار رو به چه کارهای کوچکتری میشه تقسیم کرد؟ این بخش های کوچکتر چطور و در چه زمان هایی به هم مرتبط هستند؟ چه داده ایی بین این بخش های کوچک رد و بدل میشه؟ مثلا فرض کنیم شما میخواید داده های بسیار زیادی را از منابع مختلف بخوانید و در یک فایل Log کنید؛ شما ممکنه به ازاء هر داده ایی که از یک منبع دریافت میشه، یک Task تعریف کنید. این Task ها باید داده های خودشان را به یک Task دیگه برای نوشتن در فایل تحویل بدند، تا اون Task داده ها را در فایل بنویسه. در همچین طرح ساده ایی، مشخص هست که Task نویسنده در فایل شما باید منتظر دریافت داده از Task های دیگه باشه، از طرف دیگه باید داده ها بین این Task و سایر Task ها به صورت Thread-safe منتقل بشه. یا در یک مثال دیگه، شما ممکنه بخواید یک لیست بزرگ را برای یافتن یک مقدار خاص جستجو کنید؛ شما لیست را مرتب می کنید، اون رو به چند بخش کوچک تقسیم می کنید، و هر بخش را به یک Task برای پردازش تحویل میدید. Task اصلی شما باید تا زمانی که کل لیست تمام بشه، یا یکی از این Task های کوچک نتیجه را پیدا کنه، منتظر باشه.
مفاهیمی مثل Thread مفاهیم سیستم سطح پایینی محسوب میشند که برای استفاده از اونها باید درک قابل قبولی از کارکردشان و مفاهیم پیرامون آنها داشته باشید. به جای اینکه ریسک استفاده مستقیم از Thread بدون داشتن دانش کافی را بپذیرید، برید سراغ چارچوب ها و کتابخانه هایی که امکان Parallel Programming را در یک سطح بالاتر به شما میدند؛ یعنی به جای اینکه شما به عنوان برنامه نویس درگیر این مفاهیم سطح پایین بشید، اون چارچوب خاص به شما قابلیت ها و امکاناتی میده که Task هایتان و نحوه ارتباط شان با هم را تعریف کنید، و سپس اون چارچوب هست که مشخص میکنه در سطوح پایین اون Task به چه شکل باید اجرا بشند؛ چه تعداد Thread برای انجام آنها لازم هست؟ آیا بهتر هست از Thread استفاده بشه یا Fiber؟ و مواردی از این قبیل.
تا جایی که اطلاع دارم، در دات نت Parallel Extention همچین قابلیت هایی را برای شما فراهم میکنه. خوبه که روی اون کمی وقت بذارید، و با استفاده از اون Task های خودتان را تعریف کنید، و کمتر درگیر مفاهیم سطح پایین و پیچیدگی های عجیب و غریب Multi-threading بشید. شاید بهتر باشه از لینک زیر شروع کنید:

http://msdn.microsoft.com/en-us/library/dd460717.aspx

r0ot$harp
یک شنبه 23 مرداد 1390, 23:37 عصر
از دیشب تا به حال این تاپیک دوباره جلوی چشمم ظاهر شد. از اونجایی که من برنامه نویس دات نت نیستم، معمولا توی تاپیک های این تالار پستی ارسال نمی کنم.
اما دوست عزیز، توصیه من به شما این هست که وقتی Multi-threading کار می کنید، خیلی خلیی مراقب باشید! Multi-threading چیزی نیست که اگر یک خرده چیزی ازش بدونید، برنامه تون یک خرده خوب بشه، و اگر خیلی ازش بدونید، برنامه تون کارایی اش عالی بشه! در واقع اگر از Multi-threading به اندازه کافی اطلاع نداشته باشید، استفاده از اون باعث میشه که برنامه تون به گند کشیده بشه. اگر بلد باشید و بدونید دارید چیکار می کنید، کارایی برنامه تون خیلی خوب و عالی میشه.
با این توضیح، وقتی شما میگید:

و بعدش هم اصرار می کنید که برنامه تون باید 100 Thread هم داشته باشه، من از آخر و عاقبت اون نرم افزاری که دارید می نویسید، و دردسرهایی که باید برای دیباگ کردنش بکشید، می ترسم!

Thread Pool یک کارکرد مشخص داره، اینجوری نیست که هر وقت دلمون خواست یک لیست از Thread ها داشته باشیم، اونها رو بریزیم توی یه Pool. از طرف دیگه، همینطوری که نمیشه Thread اضافه کرد؛ ساخت Thread خودش سربار بالایی داره، و از طرف دیگه، تعداد بالای Thread ها به اون معنا نیست که کارهای شما هم به همون تعداد Thread ایی که ساختید همزمان میشند؛ مثلا در یک سیستم 4 هسته ایی، یک Thread که داره روی یک هسته کار مدیریت رابط کاربر گرافیکی شما را انجام میده، سایر Thread هایی که ایجاد می کنید، باید روی همون هسته یا 3 هسته دیگه در داخل صف اجرا قرار بگیرند. حالا اگر بر فرض شما 100 عدد Thread درست کنید، این چیزی جز سر بار و شلوغ کردن اون صف ها روی این 4 هسته نیست.

الان مشخص نیست که شما دقیقا میخواید چیکار کنید؛ توصیه من به شما این هست که از مفهوم Thread بیاید بیرون؛ برای خودتون تحلیل کنید که چه کاری (Task) قرار هست انجام بشه، این کار رو به چه کارهای کوچکتری میشه تقسیم کرد؟ این بخش های کوچکتر چطور و در چه زمان هایی به هم مرتبط هستند؟ چه داده ایی بین این بخش های کوچک رد و بدل میشه؟ مثلا فرض کنیم شما میخواید داده های بسیار زیادی را از منابع مختلف بخوانید و در یک فایل Log کنید؛ شما ممکنه به ازاء هر داده ایی که از یک منبع دریافت میشه، یک Task تعریف کنید. این Task ها باید داده های خودشان را به یک Task دیگه برای نوشتن در فایل تحویل بدند، تا اون Task داده ها را در فایل بنویسه. در همچین طرح ساده ایی، مشخص هست که Task نویسنده در فایل شما باید منتظر دریافت داده از Task های دیگه باشه، از طرف دیگه باید داده ها بین این Task و سایر Task ها به صورت Thread-safe منتقل بشه. یا در یک مثال دیگه، شما ممکنه بخواید یک لیست بزرگ را برای یافتن یک مقدار خاص جستجو کنید؛ شما لیست را مرتب می کنید، اون رو به چند بخش کوچک تقسیم می کنید، و هر بخش را به یک Task برای پردازش تحویل میدید. Task اصلی شما باید تا زمانی که کل لیست تمام بشه، یا یکی از این Task های کوچک نتیجه را پیدا کنه، منتظر باشه.
مفاهیمی مثل Thread مفاهیم سیستم سطح پایینی محسوب میشند که برای استفاده از اونها باید درک قابل قبولی از کارکردشان و مفاهیم پیرامون آنها داشته باشید. به جای اینکه ریسک استفاده مستقیم از Thread بدون داشتن دانش کافی را بپذیرید، برید سراغ چارچوب ها و کتابخانه هایی که امکان Parallel Programming را در یک سطح بالاتر به شما میدند؛ یعنی به جای اینکه شما به عنوان برنامه نویس درگیر این مفاهیم سطح پایین بشید، اون چارچوب خاص به شما قابلیت ها و امکاناتی میده که Task هایتان و نحوه ارتباط شان با هم را تعریف کنید، و سپس اون چارچوب هست که مشخص میکنه در سطوح پایین اون Task به چه شکل باید اجرا بشند؛ چه تعداد Thread برای انجام آنها لازم هست؟ آیا بهتر هست از Thread استفاده بشه یا Fiber؟ و مواردی از این قبیل.
تا جایی که اطلاع دارم، در دات نت Parallel Extention همچین قابلیت هایی را برای شما فراهم میکنه. خوبه که روی اون کمی وقت بذارید، و با استفاده از اون Task های خودتان را تعریف کنید، و کمتر درگیر مفاهیم سطح پایین و پیچیدگی های عجیب و غریب Multi-threading بشید. شاید بهتر باشه از لینک زیر شروع کنید:

http://msdn.microsoft.com/en-us/library/dd460717.aspx


تشکر می کنم از شما جناب کشاورز و از دوست عزیزم محسن شامحمدی .

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

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

من خیلی فکر کردم . الان برنامه درست داره کار می کنه حتی رو Thread های بالا .

تنها مشکل همون مشکلی بود که جناب شاه محمدی عرض کردن . وقتی Thread ها با سرعت بالا ساخته می شند Queue نمی تونه بفهمه که این کدوم Thread هست . و این باعث می شه که کار به مشکل بخوره . من سورس رو تا جایی درست کردم .سورس تکمیلی رو به صورت نمونه تا چند روز آینده قرار می دم .

کاری که کردم این بود که اومدم در زمان ساخت به جای ساخت و ارجای یک Thread به Queue اومدم از یه Object Class استفاده کردم . در زمان Dqueu تمام Thread هارو با تبدیل از Object به Thread با Thread Pool ایجاد کردم .

برای این از Queue استفاده کردم تا بتونم برای شروع Thread Pool کار کرده باشم .

الان فعلا همه چی درست هست . فکر می کنم همه چی درست باشه . به نظر خودم کار کرد Thread Pool در کنار Queue کارکرد و همکاری بسیار جالب و قوی هست .

از همه دوستان و مدیران تشکر می کنم .

باتشکر احسان

vcldeveloper
دوشنبه 24 مرداد 1390, 05:45 صبح
والا در مورد اینکه بدون اینکه چیزی رو بدونم دارم شروع به کار می کنم باید بگم اینطور نیست که هیچ اطلاعاتی در این مورد ندارم .
من منظورم این نبود که هیچ اطلاعی ندارید، بلکه منظورم این بود که برای کار با Thread ها باید اطلاعات کافی داشته باشید، یعنی اگر اطلاعات ناقص داشته باشید، گیر می کنید.


والا کاری که دارم می کنم باید حتما از Thread استفاده بشه چون هیچ راهی نداره
هیچ برنامه ایی بدون Thread اجرا نمیشه، بحث من سر استفاده نکردن از Thread ها نبود، بلکه بر روی سطحی از کار هست که با اینها انجام میشه. Thread همیشه در سطوح زیرین کار شما هست، مسئله اینه که سعی کنید برای راحتی خودتون و مقیاس پذیری برنامه خودتون هم که شده، از نزدیک شدن به سطوح پایین کار با Thread ها پرهیز کنید. همونطور که در لینکی که در بالا قرار دادم هم ذکر شده، راه توصیه شده برای Parallel Programming در دات نت همون استفاده از TPL هست. از ساختارها و کلاس های سطح پایین باید برای ساخته کتابخانه هایی مثل TPL استفاده بشند، و برنامه نویس نهایی باید تا حد امکان با اون ها درگیر نشه.


الان فعلا همه چی درست هست . فکر می کنم همه چی درست باشه .
این خودش یکی از مشکلات Multi-threading هست؛ معمولا باگ ها در برنامه های Multi-threading به طور پیوسته یا در شرایط یکسان رخ نمیدن، بلکه شما ممکنه در اجراهای مختلف در زمان ها و بخش های مختلف برنامه دچار مشکل بشید. به همین جهت در برنامه های Multi-threading مهم هست که اولا همه اجزا برنامه تست بشند، و ثانیا تست ها به دفعات تکرار بشند، تا احتمال اینکه مشکلی از دست برنامه نویس در رفته باشه کاهش پیدا کنه. در هر حال، انشاء الله که برنامه تون با مشکل مواجه نشه.

r0ot$harp
دوشنبه 24 مرداد 1390, 06:00 صبح
من منظورم این نبود که هیچ اطلاعی ندارید، بلکه منظورم این بود که برای کار با Thread ها باید اطلاعات کافی داشته باشید، یعنی اگر اطلاعات ناقص داشته باشید، گیر می کنید.


هیچ برنامه ایی بدون Thread اجرا نمیشه، بحث من سر استفاده نکردن از Thread ها نبود، بلکه بر روی سطحی از کار هست که با اینها انجام میشه. Thread همیشه در سطوح زیرین کار شما هست، مسئله اینه که سعی کنید برای راحتی خودتون و مقیاس پذیری برنامه خودتون هم که شده، از نزدیک شدن به سطوح پایین کار با Thread ها پرهیز کنید. همونطور که در لینکی که در بالا قرار دادم هم ذکر شده، راه توصیه شده برای Parallel Programming در دات نت همون استفاده از TPL هست. از ساختارها و کلاس های سطح پایین باید برای ساخته کتابخانه هایی مثل TPL استفاده بشند، و برنامه نویس نهایی باید تا حد امکان با اون ها درگیر نشه.


این خودش یکی از مشکلات Multi-threading هست؛ معمولا باگ ها در برنامه های Multi-threading به طور پیوسته یا در شرایط یکسان رخ نمیدن، بلکه شما ممکنه در اجراهای مختلف در زمان ها و بخش های مختلف برنامه دچار مشکل بشید. به همین جهت در برنامه های Multi-threading مهم هست که اولا همه اجزا برنامه تست بشند، و ثانیا تست ها به دفعات تکرار بشند، تا احتمال اینکه مشکلی از دست برنامه نویس در رفته باشه کاهش پیدا کنه. در هر حال، انشاء الله که برنامه تون با مشکل مواجه نشه.

بازهم تشکر می کنم از پاسخ های کاملتون .

در مورد تست و باگ گیری من تمام Try Catch هارو با پارامتر هایی دارم کنترل می کنم . هیچ برنامه ای بدون باگ نیست . اما سعی دارم می کنم بی ایراد تا جایی که می شه کار کنه .

امشب برای شرکت فرستادم . امیدوارم مورد قبولشون باشه .

باتشکر احسان