PDA

View Full Version : سوال: serialPort1_DataReceived و thread.sleep



modern_amin
سه شنبه 02 اسفند 1401, 00:48 صبح
سلام همگی

من یک سریال پورت از یک دستگاه رو باز میکنم
و اطلاعات میفرستم (serialPort1.write) و بلافاصله در کمتر از 500میلی ثانیه دستگاه جواب رو برمیگردونه و منم میخونم (serialPort1_DataReceived)
و بعد این داده دریافتی رو آنالیز میکنم و مقادیر مورد نیازم رو میگیرم
تا اینجا کار مشکلی نیست...

مشکلم از اونجایی شروع شد که من بایست همین کار رو در سطح بالاتری انجام میدادم
من بایست ده ها دستور رو با تاخییر 1 ثانیه بفرستم و ما بین هر ارسال اطلاعات دریافتی رو آنالیز کنم.
من اومدم از thread.sleep() مابین هر ارسال دستور به دستگاه استفاده کردم اما متاسفانه اینکار باعث توقف نخ اجرایی cpu میشه و
عملا serialPort1_DataReceived رو به حالت هنگ میبره و غیرفعالش میکنه در اون لحظه..


آیا کسی از دوستان هست که تجربه این مشکل رو داشته باشه؟ ساده ترین و سریع ترین روش حل این مشکل چیه؟
آیا میشه serialPort1 خود ویژوال رو تو نخ جدا اجرا کرد؟ و آیا دستور مشابه ای هست که فقط چند لحظه تابع فعلی رو متوقف کنه و کاری به نخ و cpu نداشته باشه؟

SajjadKhati
سه شنبه 02 اسفند 1401, 07:22 صبح
سلام
از async await استفاده کنید :

هنگ برنامه هنگام گرفتن سورس سایت (https://barnamenevis.org/showthread.php?569558-%D9%87%D9%86%DA%AF-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%87%D9%86%DA%AF%D8%A7%D9%85-%DA%AF%D8%B1%D9%81%D8%AA%D9%86-%D8%B3%D9%88%D8%B1%D8%B3-%D8%B3%D8%A7%DB%8C%D8%AA&p=2449993&viewfull=1#post2449993)

اما در اون مثال ، 2 نکته را جایگزین کنید :
اول اینکه سعی کنید بجای اون بخش از کد که در نخ جدید اجرا میشه را داده بودم ، یعنی بجای کد زیر در اون مثال :



Task<string> httpDataTask = new Task<string>(new Func<string>(this.GetHttpData));
httpDataTask.Start();


از متدهای TaskFactory.StartNew یا از متد استاتیک Task.Run برای اجرای کدتوی در نخ جدید استفاده کنید :

TaskFactory.StartNew Method (System.Threading.Tasks) | Microsoft Learn (https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory.startnew?view=n et-7.0)

دوم و مهمتر اینکه سعی کنید این تیکه از کدها را وقتی بکار میبرید ، درون متدی بکار ببرید که نوع بازگشتی اش از Task یا Task<T> باشه .
یعنی مثلِ اون مثال ، مستقیما کد بالا را درون رویداد ها (که خروجی ای ندارند و void هستند) ، بکار نبرید .
چون حداقل اینکه برای مدیریت استثنا و خطاهایی که درونِ اون نخِ جدید اتفاق میافته ، نوع خروجی Task یا Task<T> بکار میاد .

برای مدیریت استثنا و خطاها در نخ جدید هم به لینک زیر مراجعه کنید :

مدیریت استثناء در نخ جدید (https://barnamenevis.org/showthread.php?572985-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%A7%D8%B3%D8%AA%D8%AB%D9%86%D8%A7%D8%A1-%D8%AF%D8%B1-%D9%86%D8%AE-%D8%AC%D8%AF%DB%8C%D8%AF&p=2456516&viewfull=1#post2456516)

برای اینکار (طبق مثال در لینک بالا) ، کافی هست که بخش await را درون try catch بذارید . اونجایی این کار را میکنید هم توی متدی باید باشه که خروجی اش از نوع Task یا Task<T> باشه .

----------

یه مثال از این نکات بالا :



private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
long fibonacciValue = await this.FibonacciAsync(37);
// کد مورد نظرتون برای اینکه بعد از اتمامِ اجرای نخ جدیدتون ، اجرا بشه را اینجا ، یعنی در پایینِ await بنویسید .
if (fibonacciValue > -1)
MessageBox.Show(fibonacciValue.ToString());
}








private async Task<long> FibonacciAsync(int number)
{
Task<long> fibonucciTask = Task.Factory.StartNew<long>(new Func<object, long>(this.Fibonacci), number);


long fibonucciReturnValue = -1;
try
{
fibonucciReturnValue = await fibonucciTask;
// کد مورد نظرتون برای اینکه بعد از اتمامِ اجرای نخ جدیدتون ، اجرا بشه را اینجا ، یعنی در پایینِ await بنویسید .
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}


return fibonucciReturnValue;
}








private long Fibonacci(object number)
{
int numberConverted = Convert.ToInt32(number);
if (numberConverted == 0 || numberConverted == 1)
return numberConverted;


return this.Fibonacci(numberConverted - 1) + this.Fibonacci(numberConverted - 2);
}


که خروجیِ await را مستقیما میتونید به عنوان مقدار long در مثال بالا ، برگردونید .
یعنی اگه متدی هم فقط از نوع Task باشه (یعنی Task<T> نباشه) ، دیگه اصلا return کردن ، لازم نداره .


میشه در این سلسله مراتبی هم یه متدی باشه که فقط Task باشه و یعنی هیچ چی برنگردونه .
یعنی مثلا رویداد کلیک در بالا ، یه متدی دیگه ای که در مثال بالا نیست و فرضا اسم اش MiddleMethod باشه که خروجی اش از نوع Task باشه (نه از نوع Task<T> یا Task<long>) را فراخوانی کنه .

بنابراین این متد ، اصلا نیاز به استفاده از کلمه ی کلیدی return در بدنه اش نداره (ربطی به استفاده نکردن از کلمه ی کلیدیِ await نداره و باید ازش استفاده کنه . در واقع توصیه ی اکید میشه که تمام متدهایی که async هستند ، درون شون از await استفاده کنند) .

و این متدِ MiddleMethod ، درون خودش ، متدِ FibonacciAsync در مثال بالا را فراخونی کنه که بقیه ی روال هاش مشخص هست .

متد FibonacciAsync در بالا را بصورت خلاصه به شکل زیر هم میشه نوشت منتها در این حالت ، نمیشه از try catch درش استفاده کرد یا بعد از await اش ، کدی نوشت تا بعد از خاتمه ی اجرای نخ جدید ، اون کد اجرا بشه :



private async Task<long> FibonacciAsync(int number)
{
return await Task.Factory.StartNew<long>(new Func<object, long>(this.Fibonacci), number);
}


---------

await هم کارکردش به این صورت هست که وقتی کنترل برنامه ، به کلمه ی کلیدی await میرسه ، اون Task ئه اجرا شده ای که بهش داده شد را منتظرش میمونه تا اجراش تمام بشه و بنابراین کدهایی که در پایینِ اش نوشته شدن را معلق میذاره (یه کاری شبیه به yield ها) و این کدها اجرا نمیشن تا اون Task خاتمه پیدا کنه ،

و مخصوصا اینکه همچنین کنترلِ اجرای کدها را به استکِ متدِ قبلی هدایت میکنه (یعنی متد قبلی توی استک که فراخوانی کننده ی این متدی بود که توی این متد ، کلمه ی کلیدیِ await وجود داشت ، کنترلِ اجرای کدهای برنامه را به اون استکِ قبلیِ متد در اون نخ میبره) .
یعنی کاری شبیه به return انجام میده (البته از لحاظ برگشتن به استک قبلی ، شبیه بهش هست وگرنه ادامه ی کدهاش بعد از خاتمه ی نخ اجرا میشه که این ، کار را return انجام نمیده) .

-----------

برای مدیریت دسترسی به داده ی واحد (مثلا دسترسی به متغییر سراسری ، یا دسترسی به متغییر محلی ای که اشاره گری به متغییر سراسری هست) درون دو یا چند نخ هم روش های متفاوتی که هر کدوم برای جایی کاربرد دارن ، هست اما یکی از رایج ترین شون ، استفاده از کلمه ی کلیدی lock هست .