PDA

View Full Version : پیاده سازی تایمر با استفاده از Thread



Ghasem Dehghani
پنج شنبه 13 اردیبهشت 1386, 08:09 صبح
با سلام به همه دوستان.
یک سوال زیبا و جالب داشتم و اونم این سوال است که اگر شما از یک تایمر برای مثلا نمایش ساعت هر یک ثانیه یک بار در یک Label استفاده کنید می بینید در (Windows Task Manager) که با Ctrl+Alt+Del باز میشود در محل Performance قسمت Cpu Usage را مشاهده کنید خواهید دید که تنها 10% از Cpu را برای انجام محاسبات خود میگیرد .
تا اینجا که مشکلی نسیت درست.:لبخندساده:
اما مشکل از اینجا شروع میشود که اگر از یک Thread برای انجام نمایش ساعت در هر یک ثانیه استفاده کنم با کمال تعجب اون 10% قبلی به 100% تبدیل خواهد شد . :متعجب:
حالا سوال من این است که تایمر از چه روشی برای محاسبات خود استفاده میکند و اگر میشود با یک کد مثلا همون مثال بالا را با یک Thread نمایش دهید فکر کنم که این سوال برای بسیاری از برنامه نویسان جالب باشد البته ممکن است خیلی از برنامه نویسان حرفه ای مثل شما دلیل و راه حل آن را بدانید ولی خیلی ها هم مثل من از این امر اطلاع ندارد و با پرسیدن یاد میگیرند .
با سپاس و تشکر فراوان . :بوس:

newgoldenman
پنج شنبه 13 اردیبهشت 1386, 09:03 صبح
دوست عزیز مگر شما تایمرتان را خودتان نوشته اید؟!اگر چنین است که بعید میدونم، پیشنهاد میکنم از ابزار DateAndTimePicker استفاده کنید! که در این صورت CPU Usage شما هیچ تکانی نخواهد خورد(نسبت به وضع جاری)!

Ghasem Dehghani
پنج شنبه 13 اردیبهشت 1386, 09:44 صبح
نه این فقط یک مثال بود و منظور اصلی من چیز دیگری است .
منظورم این بود که عمکرد تایمر تا اونجایی که من میدونم مثل یک Thread است یعنی در عین جدا بودن از برنامه (به صورت موزای چک نمودن زمان تنظیم شده برای انجام یک کد و بدون ایجاد هیچ اختلالی در برنامه تا رسیدن به زمان مورد نظر) کار چک نمودن زمان وارد شده در Interval تایمر که بر حسب میلی ثانیه مثلا 1000 میلی ثانیه یک بار است .
خوب اگر که قرار باشد ما یک تایمر را با Thread درست کنیم به شکل زیر عمل میکردیم :
1-یک if را تا زمان رسیدن به زمان یک ثانیه چک کنیم (در حد ساده ساعت سیستم را چک کنیم اگر تغییر کرد if دستوراتش را اجرا کند .)
2-یک دستور خاص را انجام دهیم
و این Thread تا آخر برنامه در حال انجام محاسبات فوق است .
در حد ساده این طوری میشه درسته .
حالا CPU Usage تایمر ما با تایمر خود #C خیلی متفاوته و سوال من هم همین جاست که چطوری میشه که این دو اینقدر متفاوت عمل میکنند و روی CPU تاثیرات متفاوتی را دارند . مگر تایمر خود #C در مورد همین مثال چطوری کد نویسی شده است
حالا خودتون میتونید هر مثال دیگری رو طرح کنید و آزمایش نمایید تقریبا تمام تردهایی که مینویسید CPU Usage را در وضعیت 100% خواهد داد .
جواب به این سوال میتواند به کاربران در ایجاد برنامه های قوی و در عین حال بهینه شده کمک نماید.
( داخل پرانتز یه مثال متفاوت را مطرح میکنم تا هدف از طرح این سوال را بهتر متوجه شوید تقریبا همه شما با برنامه های ضبط صدا آشنایی دارید این برنامه ها برای ضبط صدا باید اطلاعات کارت صوتی را در هر ثانیه مثلا 20000 بار در ثانیه نمونه برداری میکنند حالا پیاه سازی این برنامه با یک تایمر تقریبا غیر ممکن است و شما مجبور به استفاده از Thread هستید که با این کار دوباره مشکلات قبلی مطرح شده تکرار میشود . (این هم فقط یک مثال است)
جالب اینجاست که برنامه های ضبط صدا با حداکثر 40% CPU Usage این کار را حتی با نمونه برداری های بالاتر در حدود 115000 نمونه برداری در ثانیه انجام میدهند و اینقدر کارآیی CPU را پایین نمی آورند)
روش اینگونه برنامه ها به چه صورت است لطفا اگر میشود با یک کد توضیح دهید .
ممنون

Ghasem Dehghani
پنج شنبه 13 اردیبهشت 1386, 10:43 صبح
لطفا یکی بیاد وسط و راهنمایی کنه .

omid_Ahmadi
پنج شنبه 13 اردیبهشت 1386, 12:51 عصر
می تونید ترد رو با استفاده از دستور System.Threading.CurrentThread.Sleep() بخوابونید به مدت زمان مورد نظرتون. (اگر آدرس متد رو اشتباه تایپ کرده باشم می تونید با استفاده از Object Browser اون رو پیدا کنید)

ARA
پنج شنبه 13 اردیبهشت 1386, 16:10 عصر
چون private message زده بودی برام برای اینکه جوابت رو بدم یک مثال آماده کرده بودم ولی وقتی که میخواستم update کنم :گیج:دیدم اقای احمدی لپ کلام رو گفتن:کف: ممنون ولی خوب بگذار کاملتر جوابت رو بگیری
اول روش خودت
به مثال زیر نگاه کن


long j = 0;
private void button2_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(showClock);
thread1.Start();
Thread.Sleep(1000);
thread1.Abort();
label2.Text = j.ToString();
}
private void showClock()
{
while (true)
j++;
}




یک ترد فعال شده و داخل حلقه این کارها را تکرار میکند
یک مقایسه و یک افزایش تا زمانی که این ترد را abort کنیم
و در قسمت اصلی زمانی که thread1 فعال شده ترد اصلی sleep شده به مدت یک ثانیه و بعد از یک ثانیه فعال شده thread1 رو از بین برده و شماره j را به ما نشان میدهد
به نظر شما چند مقایسه و افزایش رخ داده
در سیستم من حدود celeron 1.7 GH عدد j=97,695,356 یعنی در برنامه ای که شما نوشتین در 1 ثانیه اینقدر کار انجام شده توسط cpu داریم


حال به این نگاه کن


private void button2_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(showClock);
thread1.Start();
}
private void showClock()
{
while (true)
{
Thread.Sleep(1);
labelwriter(DateTime.Now.TimeOfDay);
}
}
delegate void LableWriterDelegate(TimeSpan time);
public void labelwriter(TimeSpan time)
{
LableWriterDelegate sb = new LableWriterDelegate(lableUpdate);
object[] args = new object[1];
args[0] = time;
this.label2.Invoke(sb, args);
}
public void lableUpdate(TimeSpan time)
{
label2.Text = time.ToString();
}


1000 بار در ثانیه lable2 رو update میکنیم ولی cpu usage من 5% رو نشون میده امتحانش کن :چشمک:حالا فهمیدی چرا سی پی یوت آمپر چسبونده :لبخندساده:

Ghasem Dehghani
پنج شنبه 13 اردیبهشت 1386, 17:15 عصر
قبل از هر چیز باید از مدیر محترم بخش و بخصوص آقای ARA تشکر و قدر دانی ویژه نمایم که این قدر با حوصله پاسخ داده اند . :قلب:
اما .................
تا اینجا که اگر خواسته باشیم تایمر را پیاده سازی کنیم کاملا عالی جواب میدهد ولی زمان Sleep بر حسب میلی ثانیه است یعنی اگر عدد 1 را وارد کنیم خواهیم دید که در هر ثانیه دستورات 1000 بار اجرا میشود .
و اگر 1000 را برای Sleep وارد کنیم در هر ثانیه 1 بار اجرا میشود . تا اینجا را خوب فهیمدم .
اما اگر خواسته باشیم مثلا نمونه برداری از کارت صوتی را در هر ثانیه مثلا 44100 بار در ثانیه برای فرمت MP3 داشته باشیم باید فواصل نمونه برداری منظم باشد و دقیقا همان تعداد را در ثانیه نمونه برداری کنیم .
حال اگر Sleep را روی یک قرار دهیم در هر ثانیه فقط 1000 نمونه برداری را داریم . در حالی که ما میخواهیم 44100 بار در ثانیه را داشته باشیم اگر به این سوال هم جواب بدین خیلی از شما ممنون میشم .
(به جای مثال نمونه برداری میتونیم یک متغییر را مثلا i را طوری در یک ثانیه ++i کند که در پایان یک ثانیه مقدار آن 44100 باشد )
باز هم از تمامی عزیزانی که مرا راهنمایی کرده اند کمال تشکر را دارم .

Alireza_Salehi
پنج شنبه 13 اردیبهشت 1386, 20:57 عصر
اگر با صوت کار میکنید:
sampling یه کار سخت افزاریه ، کاری که شما دنبالشی Quantization هستش که یک کار نرم افزاریه و تایمر و امثالهم نیازی نداره!

سخت افزار با یک دقتی(Sampling Rate) صدا رو از آنالوگ به دیجیتال تبدیل میکنه ،

بعدا وقتی شما می خواهید این نمونه ها (Samples) رو با دقت های مختلف در فرمت های مختلف ذخیره کنید به روش Quantization دقت نمونه ها (Samples) رو تغییر میدید!

hdv212
جمعه 14 اردیبهشت 1386, 02:03 صبح
ممکنه صریحا بگید شما دنبال چی هستید ؟ با توجه به نوشته هاتون شما بهتره از کامپوننت هایی که برای کار با صدا طراحی شده اند استفاده کنید، یکی از دلایل بالا بودن CPU Usage در مثال شما، نوع الگوریتم اجرایه، دقت کنید که در اگر CpuUsage خیلی زیاده، ولی معمولا پردازنده ها HyperThreading هستند و میتونن در آن واحد چندین رشته رو با هم اجرا کنن، باز اگه هدفتون رو بیشتر روشن کنید، شاید بتونم کمکتون کنم.

Ghasem Dehghani
جمعه 14 اردیبهشت 1386, 07:16 صبح
باز هم سلام .
بعضی از دوستان سوال کرده بودند که هدف از طرح این سوال چه بوده است هدف اصلی از طرح این سوال افزایش آگاهی در زمینه Thread بوده است و دانستن علت اشکالات به وجود آمده همانطور که مطرح کره بودم ، که آقای ARA با مثال های خوب خود به خوبی اشکالات من را در کد و بخصوص علت آن اشکال که اجرای بیش از حد دستورات در یک بازه زمانی بوده است را به بنده گوش زد کرده اند . و اطلاعات مرا در این زمینه بسیار بالا برده اند .
طرح مثال نمونه برداری از کارت صوتی نیز فقط یک مثال بود و همین و بس همانطور که قبلا هم گفتم .

(در صورتی که آقای hdv212 هم درست گفته باشند [ که معمولا پردازنده ها HyperThreading هستند] اما وقتی که کارمان با 40000 محاسبه در ثانیه راه می افتد چرا با تحقیقی که آقای ARA در مورد مثال تایمر در بالا انجام داه اند در حدود 98 میلیون محاسبه در ثانیه را به CPU تحمیل کنیم !!!! که این عدد در CPU های بالاتر قطعا عدد نجومی تری میشود
- حالا یه لحظه خودتون را جای CPU بذارین ! )

با این تفصیرات میخواستم گفته های آقای ARA را تکمیل تر کنید تا بدانم اگر خواسته باشم Sleep را در بازه زمانی معیین طوری تعیین کنیم که دستورات بیش از 1000 بار در ثانیه اجرا بشن مثلا 40000 بار در ثانیه و همون Cpu Usage قبلی را که بسیار بهینه بوده است را داشته باشیم چه کار باید بکنیم (بهینه سازی در استفاده از نرم افزار و سخت افزار) .
که اگر به این سوال من هم جواب بدین دیگر مبحث Thread ها را با کمک شما دوستان تا اون سطحی که مورد قبول خودم باشه یاد گرفته ام و این تاپیک هم میتونه مرجع کاملی در رابطه با Thread برای سایر دوستانی که از Thread آگاهی ندارن و حتی موارد استفاده اون رو هم نمیدونن میتواند مفید باشد .

anubis_ir
جمعه 14 اردیبهشت 1386, 09:03 صبح
دوست عزیز ،
اگر نیاز به thread timer دارید، دات نت کلاس زیر را به صورت استاندارد دارد :
System.Threading.Timer (http://www.google.com/search?q=System.threading.timer&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a)

omid_Ahmadi
جمعه 14 اردیبهشت 1386, 09:09 صبح
ببینید، این تردهایی که در .NET مشاهده می کنید رزولوشنی با دقت یک میلی ثانیه دارن، که از کلاک سیستم گرفته میشه. تا جایی که اطلاع دارم در ویندوز به صورت عادی نمی تونید رزولوشن این کلاک رو به مقداری کمتر از یک میلی ثانیه تغییر بدید، که البته با توجه به روشی که ویندوز با استفاده از اون ترد ها رو زمان بندی می کنه، حتی اگر بتونید هم این کار رو بکنید هیچ تضمینی نیست که تردها در زمان مورد نظر شما اجرا بشن.

برای اینکه بتونید زمان این مورد رو به زیر یک میلی ثانیه تغییر بدید، می تونید از ران تایمهایی که می تونن در این زمینه کمک کنن استفاده کنید. توی ویندوز سیستم هایی مثل RTX وجود دارن که می تونن در پیاده سازی سیستمهای Soft RealTime (به گونه ای که اجرای دقیق ترد در زمان مشخص شده چندان اهمیت نداشته باشه) و یا Hard RealTime ( به گونه ای که ترد دقیقا در زمان مشخص شده اجرا بشه) به شما کمک کنن.

این مدل سیستم ها می تونن دقتی به اندازه کلاک میکرو کنترولر 8254 رو به شما بدن، یعنی ضریبی از حدودا 800 نانو ثانیه (مقدار دقیقش رو نمی دونم). به این ترتیب برای مثال می تونید تردی داشته باشید که در هر 100 میکرو ثانیه اجرا بشه (بر حسب نوع کلاکی که استفاده می کنید). البته این موارد در .NET قابل استفاده نیست و فقط میشه پروسس مربوط به اون رو با استفاده از IPC به پروسس ایجاد شده در .NET مرتبط کرد تا به این ترتیب به مزیتهای هر دو دسترسی داشته باشید.

Ghasem Dehghani
جمعه 14 اردیبهشت 1386, 11:00 صبح
حالا اگر دقیق هم در زمان مشخص شده نتونست اشکالی نداره ولی در همون حد و حدود هم اگر بود اشکالی نداره اگر میشه با کد توضیح بدین .

omid_Ahmadi
جمعه 14 اردیبهشت 1386, 12:19 عصر
در مورد سیستم های Hard RealTime در ویندوز NT می تونی اینجا (http://msdn2.microsoft.com/en-us/library/ms838340.aspx) رو ببینی

Ghasem Dehghani
شنبه 15 اردیبهشت 1386, 08:24 صبح
با سلام خدمت مدیر محترم بخش .
ممنون از راهنمایی های سازنده شما ولی توی سایتی که معرفی کردین اطلاعاتی بیشتر از آنچه که شما به صورت فارسی توضیح دادین نتونستم به دست بیارم (بیشتر در حد تئوری ) ولی برای من که تازه قدم در راه برنامه نویسی گذاشته ام قابل فهم برای پیاده سازی عملی نبود لطفا اگه میشه با یک مثال عملی شما و یا سایر دوستان یک تیر خلاص به این تاپیک بزنید . :چشمک:

ARA
شنبه 15 اردیبهشت 1386, 09:22 صبح
سلام

ببینید اونجوری که شما میخواین یک برنامه multithread میتونه کمکتون کنه

thread pool هم گزینه خوبیه اگه کارها کوچک باشند مثل یک نمونه برداری و ذخیره آن
فقط فکر کنم هماهنگ کردنشون یک کم دردسر باشه
که اونهم از
AutoResetEvent مربوط به thread ها میتوان کمک گرفت و همچنین مبحث قفل گذاری ها بشه حلش کرد

omid_Ahmadi
شنبه 15 اردیبهشت 1386, 16:27 عصر
دوست عزیز،
مشکل ایشون تنظیم ترد به گونه ای هست که در هر ثانیه بیش از 1000 بار اجرا بشه، یعنی مقدار Sleep رو کوچکتر از یک میلی ثانیه وارد کنن.

من اینن کار رو در .NET انجام ندادم، تا جایی هم که من می دونم به سادگی فراخوانی یک تابع و پاس کردن یک سری پارامتر نیست، بهتره لینکی که دادم رو به دقت مطالعه کنید، یه مقدار هم روی اینترنت بگردید، می تونید اطلاعات بیشتری بدست بیارید.

موفق باشید

PC2st
شنبه 15 اردیبهشت 1386, 16:55 عصر
در تکمیل گفته های آقای احمدی، برای توقف با دقتی در حد نانو ثانیه (دقیقا نمیدونم ولی خودش نوشته 100-nanoseconds unit)، میشه به شکل زیر عمل کرد:


Thread.Sleep( new TimeSpan( yourTime ) );

yourTime زمانی هست که میخوای متوقف باشه (فکر کنم با دقت 100 نانو ثانیه باشه)

ARA
شنبه 15 اردیبهشت 1386, 17:40 عصر
منهم منظورش رو گرفتم
به همین خاطر پیشنهاد مولتی ترد دادم که اگه ده ترد به فاصله های زمانی کمتر از 1 میلی ثانیه با هم اجرا بشند میشه رقم کمتری بدست آورد

که البته



دوست عزیز،
مشکل ایشون تنظیم ترد به گونه ای هست که در هر ثانیه بیش از 1000 بار اجرا بشه، یعنی مقدار Sleep رو کوچکتر از یک میلی ثانیه وارد کنن.



با کمک دوست عزیزمون PC2st.ir فهمیدم راه دیگری نیز هست اتفاقا چک کردم بسیار جالب بود ممنون :قلب:




کد:
Thread.Sleep( new TimeSpan( yourTime ) );
yourTime زمانی هست که میخوای متوقف باشه (فکر کنم با دقت 100 نانو ثانیه باشه)