PDA

View Full Version : استفاده از دو thread در برنامه



combo_ci
جمعه 29 تیر 1386, 15:06 عصر
سلام
من یه برنامه نوشتم که توی یکی از فرم هاش یک data grid رو fill میکنم. (برنامه رو با 2005 نوشتم ) .میخواستم توی اون فرم یه thread تعریف کنم که datagride توی اون thread پر شه.... اما با یه خطا مواجه شدم (عکس خطا رو ضمینه تاپیک کردم)......از background worker هم استفاده کردم.....اما بازم همون خطا رو داد....


اینم عکس خطا :

http://virga.persiangig.com/image/error.PNG

میشه راهنماییم کنین که چطوری میشه با یه thread دیگه به یکی از control های فرم دسترسی داشته باشیم ....

mehdi.mousavi
جمعه 29 تیر 1386, 15:50 عصر
سلام
من یه برنامه نوشتم که توی یکی از فرم هاش یک data grid رو fill میکنم. (برنامه رو با 2005 نوشتم ) .میخواستم توی اون فرم یه thread تعریف کنم که datagride توی اون thread پر شه.... اما با یه خطا مواجه شدم (عکس خطا رو ضمینه تاپیک کردم)......از background worker هم استفاده کردم.....اما بازم همون خطا رو داد....
میشه راهنماییم کنین که چطوری میشه با یه thread دیگه به یکی از control های فرم دسترسی داشته باشیم ....

سلام.
واقعیت اینه که وقتی شما یه Window جدید ایجاد می کنید، دیگه نمیتونید به اون Window از طریق thread های دیگه دسترسی پیدا کنید. هر تلاشی برای دسترسی به پنجره (یا هر کنترلی در main-thread) باعث میشه تا شما پیام cross-thread operation not valid رو بگیرید. متاسفانه مایکروسافت در عین ناباوری یه Property به اسم CheckForIllegalCrossThreadCalls روی کنترل گذاشته که اگه اونو false کنید، دیگه این Checking انجام نمیشه و به نظر مشکلی پیش نمیاد. اما این روش صحیحی برای پیاده سازی cross-threading access نیست. چون شما از BackgroundWorker استفاده می کنید، من هم طرز استفاده صحیح از این کلاس رو بهتون می گم، و الا در بقیه موارد (مثل استفاده از Thread و ...) باید روشهای دیگه ای رو در پیش بگیرید. در واقع کاری که شما می خواهید انجام بدید و زمانگیر هستش، دریافت اطلاعات از سرور (یا بانک، بسته به نوع طراحی) هستش؛ نه پر کردن DataGrid. در نتیجه شما در Event DoWork باید اطلاعات رو از سرور دریافت کنید، و هر از گاهی اون اطلاعات رو به Grid اضافه کنید (مثلا هر 100 ردیف یکبار). Pseudo Code زیر مفهوم چیزی رو که گفتم بهتر نشون میده:



OnDoWork(...)
{
while(data to be fetched, exists)
{
data = GetOneHundredRowsFromDataSource();
ReportProgress(0, data);
}
}

private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
//This methods is called in the main-thread context...

data = e.UserState;
addDataToTheGridControl();
}

به این ترتیب وقتی شما تابع ReportProgress رو دز DoWork صدا میزنید، در واقع باعث میشید تا اطلاعات مورد نطرتون (که در پارامتر دوم این تابع بهش میدید) به main-thread برنامه، marshal بشه و در دسترس قرار بگیره. به این ترتیب، متود OnProgressChanged (وProgressChanged event) صدا زده میشه و شما میتونید در این متود بدون نگرانی به کنترلهای main-thread (و از اون جمله Grid Control) دسترسی پیدا کنید.

Alireza_Salehi
جمعه 29 تیر 1386, 15:55 عصر
این یک راه حل:
http://www.codeproject.com/useritems/AccessControlFromThread.asp

ولی به طور کلی اینجا رو ببین:
How to: Make Thread-Safe Calls to Windows Forms Controls (http://msdn2.microsoft.com/en-us/library/ms171728.aspx)

combo_ci
جمعه 29 تیر 1386, 16:37 عصر
خیلی ممنونن از راهنماییتون دوستان

آخه این مشکل تنها توی کارهای data base به وجود نمیاد ....من یه برنامه دیگه هم نوشته بودم که توش یه فیلم رو لود میکردم....چون عمل لود کردن فیلم یه کم طولانی بود میخواستم اووونو توی یه thread جداگانه بزارم که باز با همین خطا مواجه شدم.....واقعا گیج شدم:اشتباه:

از این مثال دوست خوبم آقای علیرضا صالحی که تو code project بود چیز زیادی نفهمیدم ......میشه یه win app کوچولو که از اون کد استفاده کردین اینجا بزارین ؟

ARA
شنبه 30 تیر 1386, 10:29 صبح
مشکل cross thread همونطوری که تو پیغام error نوشته فقط باید چک کردن اون رو در لود فرم false کنی



form1.CheckForIllegalCrossThreadCalls = false;

Alireza_Salehi
شنبه 30 تیر 1386, 13:21 عصر
خیلی ممنونن از راهنماییتون دوستان

آخه این مشکل تنها توی کارهای data base به وجود نمیاد ....من یه برنامه دیگه هم نوشته بودم که توش یه فیلم رو لود میکردم....چون عمل لود کردن فیلم یه کم طولانی بود میخواستم اووونو توی یه thread جداگانه بزارم که باز با همین خطا مواجه شدم.....واقعا گیج شدم:اشتباه:

از این مثال دوست خوبم آقای علیرضا صالحی که تو code project بود چیز زیادی نفهمیدم ......میشه یه win app کوچولو که از اون کد استفاده کردین اینجا بزارین ؟
لینک دوم رو با دقت بخونید بیشتر توضیح داده.اگر باز هم متوجه نشدید بگید مثال بزارم.



مشکل cross thread همونطوری که تو پیغام error نوشته فقط باید چک کردن اون رو در لود فرم false کنی



form1.CheckForIllegalCrossThreadCalls = false;


عزیزم بیکار نبودن که این رو گذاشتن، همینجوری که یه چیزی رو disable نمی کنند یه کم زحمت داره ولی اون لینک دوم رو که گذاشتم بادقت بخونید ، راه اصولی تری برای حل این مشکل وجود داره.

ARA
شنبه 30 تیر 1386, 15:27 عصر
هر چیزی بهایی داره
برای یک برنامه ساده خیلی مواقع نمی صرفه راه های سخت رو امتحان کرد ،چه بسا با سر در نیاوردن از اون کد از C# بیزار بشیم و خسته شیم
ولی با راههای ساده شروع کرده و مسئله های سخت تر را راحت تر میبینیم

من کلی با اینها سرو کله زدم و راحت ترین راه رو براش گفتم شایددر یک برنامه ساده کسی نتونه بگیه که کجای این راه حل مشکل داره

کسی هم که برنامه درست و حسابی بنویسه (سوءتفاهم نشه) تو این جور چیزا نیمونه

ولی ممنون که یک راه هم شما پیشنهاد کردین

combo_ci
سه شنبه 02 مرداد 1386, 11:17 صبح
من توی vs 2003 چک کد زیر رو پیدا نکردم.....لطف میکنین راهنماییم کنین؟


form1.CheckForIllegalCrossThreadCalls = false;

ARA
سه شنبه 02 مرداد 1386, 11:21 صبح
تو 2005 فقط جواب میده به نظرم تو 2003همچین مشکلی نداشیم

combo_ci
سه شنبه 02 مرداد 1386, 12:17 عصر
آقای صالحی لطف میکنین یه تیکه کد بزارین ( طبق همون مثالی که واسه multithread زدید که از یک delegate استفاده کردین) که یه data gride توی یه thread جدا گونه لود بشه
ممنون

mehdi.mousavi
سه شنبه 02 مرداد 1386, 12:21 عصر
من توی vs 2003 چک کد زیر رو پیدا نکردم.....لطف میکنین راهنماییم کنین؟

form1.CheckForIllegalCrossThreadCalls = false;


سلام.
متعجبم که چرا جوابی رو که دادم نخوندید و هنوز اصرار به انجام کار نادرست دارید.

اولا، بودن یا نبودن یه feature در .net framework هیچ ربطی به نسخه کامپایلر نداره، در نتیجه جمله "من تو vs 2003 چک کردم و پیدا نکردم..." درست نیست. جای این جمله باید بگید "من این property رو تو dotnet framework 1 و 1.1 پیدا نکردم، فقط توی 2 دیدمش!".

دوما، بادید عرض کنم که CheckForIllegalCrossThreadCalls رو گذاشتن چون تو dotnet framework 1 به اشتباه اجازه Illegal Cross Threading رو داده بودن. اگه این اشتباه صورت نمی گرفت، بحث backward compatibility هم پیش نمیومد، و در نتیجه هرگز چنین Property زشتی رو روی کنترل نمیدیدیم. در واقع در نسخه 1 و 1.1، دسترسی شما از یک thread به کنترلی در یک thread دیگه باعث بروز خطا نمی شد (منظورم خطای پیش بینی شده هستش) و چنین Property ای هم وجود نداشت. اما چون این مساله باعث شد تا خیلی از برنامه نویسهای بی تجربه مدام این عمل رو تکرار کنن، مایکروسافت تصمیم گرفت تا به محض تشخیص چنین فراخوانیهای نادرستی یه خطا throw کنه و ... در نتیجه برنامه نویس متوجه بشه که کاری که داره انجام میده صحیح نیست و روش درستی رو برای مارشالینگ اطلاعات بین دو thread انتخاب کنه. اگر به توضیح این Property در msdn هم نگاه کنید، بیشتر متوجه مساله میشید:

"هنگامیکه thread ای به Property ها و متودهای کنترلی که در thread دیگری ایجاد شده دسترسی پیدا کند، اغلب دچار نتایج پیش بینی نشده ای می گردد. یک اشتباه متعارف در استفاده از thread ها، فراخوانی متودهایی است که به هندلهای یک کنترل در thread نامناسب دسترسی می یابد. Property ای CheckForIllegalCrossThreadCalls را true کنید تا بتوانید فعالیت thread فعلی را ساده تر شناسایی کنید".

در donet framework 1 و 1.1 هم این مساله وجود داشت. یعنی شما هیچ ایرادی نمیدید در دستیابی به کنترلهایی در thread های دیگه، اما هر لحظه این امکان وجود داشت تا دچار "نتایج پیش بینی نشده" (مثل hang کردن برنامه، reset شدن app و ...) بشید.

سوما، من به زبون شیرین فارسی براتون توضیح دادم که چطور از BackgroundWorker استفاده کنید ولی شما هنوز اصرار دارید تا به نحوی با حداقل فعالیت برنامه اتون رو به وضعیت running ببرید. (در حالیکه از Background Worker هم دارید به اشتباه استفاده می کنید). حالا با این کار، شما دو اشتباه رو تو برنامه اتون دارید مرتکب می شید، یکی استفاده نادرست از BackgroundWorker و دیگری بوجود آوردن شرائط ناشناخته در برنامه.

حالا دیگه راه و چاه رو بلدید، اگرهنوز اصرار دارید که متغیر مربوطه رو false کنید که برنامه اتون کار کنه، خوب اینکارو بکنید و دعا کنید که هیچوقت دچار اون شرائط پیش بینی نشده نشید.

combo_ci
سه شنبه 02 مرداد 1386, 12:39 عصر
ای بابا چرا عصبانی میشید برادر

من اصل برنامم تو 2003 هستش و برا اینکه نحوه کار background wroker رو چک کنم توی 2005 هم برنامه رو کامپایل کردم اما باز هم همون خطای 2003 رو داد.....شما یه بار تست بکنید حرف من رو اونوقت میبینید که توی .net framwork 1.1 هم همچین خطایی رخ میده ...البته توی 2005 هم با false کردن پراپرتی CheckForIllegalCrossThreadCalls هم اتفاق خاصی در برنامه رخ نمیده ....انگار که شما یک try ..catch بنویسید و توی catch هیچی ننویسید ....در واقع کار با این کار برنامه از خطای cross thread رد میشه

http://msdn2.microsoft.com/en-us/library/ms171728.aspx

این مثال آقای صالحی خیلی جالب قضیه رو توضیح داده بود ...اما من دقیقا متوجه نشدم که delegate به کار رفته توی اون برنامه می text box رو set میکنه.

واسه همین گفتم که یه تیکه کد که من بفهمم یه data ride رو چه طوری با وجود اون delegate پر کرد

ممنون

mehdi.mousavi
سه شنبه 02 مرداد 1386, 15:12 عصر
ای بابا چرا عصبانی میشید برادر

من اصل برنامم تو 2003 هستش و برا اینکه نحوه کار background wroker رو چک کنم توی 2005 هم برنامه رو کامپایل کردم اما باز هم همون خطای 2003 رو داد.....شما یه بار تست بکنید حرف من رو اونوقت میبینید که توی .net framwork 1.1 هم همچین خطایی رخ میده ...البته توی 2005 هم با false کردن پراپرتی CheckForIllegalCrossThreadCalls هم اتفاق خاصی در برنامه رخ نمیده ....انگار که شما یک try ..catch بنویسید و توی catch هیچی ننویسید ....در واقع کار با این کار برنامه از خطای cross thread رد میشه

http://msdn2.microsoft.com/en-us/library/ms171728.aspx

این مثال آقای صالحی خیلی جالب قضیه رو توضیح داده بود ...اما من دقیقا متوجه نشدم که delegate به کار رفته توی اون برنامه می text box رو set میکنه. واسه همین گفتم که یه تیکه کد که من بفهمم یه data ride رو چه طوری با وجود اون delegate پر کرد

ممنون

سلام.
من تو پست قبلی مگه توضیح ندادم که چه اتفاقی می افته اگه اونو false کنید؟ حتی یه پاراگرف از msdn رو هم واستون ترجمه کردم، که از اشتباه در بیایین، اونوقت بازم میگین "با false کردن CheckForIllegalCrossThreadCalls هیچ اتفاقی خاصی در برنامه رخ نمیده". کی اینو گفته؟ مدرکتون برای این حرف کجاست؟ چرا شایعه درست می کنید؟ فکر نمیکنید 4 نفر دیگه این نوشته شما رو بخونن به اشتباه می افتن، (اگر تحقیق نکنن)؟ خوب این چیو نشون میده؟ خودتون قضاوت کنید.