PDA

View Full Version : سوال: BackgroundWorker



prans_tork
دوشنبه 02 فروردین 1395, 17:33 عصر
با سلام خدمت دوستان

مگر نه اینکه شی BackgroundWorker به نوعی برای اجرای کدها در پشت صحنه کدهای اصلی و روند اصلی برنامه هست بدون اینکه روند اجرای اصلی برنامه مختل بشه و یا به عبارتی انجام نوعی پاراللینگ در کنار thread اصلی بدون هنگ کردن برنامه.
من در برنامم به محض کلیک بر روی کلیدی که برای بازسازی دیتابیس قرار دادم و در رویداد کلیک اون BackgroundWorker رو به صورت RunWorkerAsync اجرا کردم و کدهای مربوط به بازسازی دیتابیس رو در قسمت DoWork نوشتم، برنامه تا اجرای کامل عمل بازسازی، هنگ میکنه


مشکل از کجاست؟

ژیار رحیمی
سه شنبه 03 فروردین 1395, 06:55 صبح
سلام کدی که نوشتی بزار تا بررسی شود مشکل از کجاست.

prans_tork
سه شنبه 03 فروردین 1395, 14:17 عصر
سلام کدی که نوشتی بزار تا بررسی شود مشکل از کجاست.
ممنونم از توجهت دوست عزیز

ابتدا به عنوان یه پروپرتی خارج از متد سازنده فرم تعریفش کردم:


#region Propertice


BackgroundWorker workerMainForm = new BackgroundWorker();

#endregion




سپس در متد سازنده فرم خطوط زیر رو دارم:


workerMainForm.WorkerReportsProgress = true;
workerMainForm.WorkerSupportsCancellation = true;
workerMainForm.DoWork += new DoWorkEventHandler(workerMainForm_DoWork);
workerMainForm.ProgressChanged += new ProgressChangedEventHandler(workerMainForm_Progres sChanged);
workerMainForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerMainForm_RunW orkerCompleted);


بعد در متد کلیک کلید مورد نظرم به صورت زیر:


private void btnDBRecovery_Click(object sender, EventArgs e)
{
MessageBoxEx.EnableGlass = false;
DialogResult dr = MessageBoxEx.Show("آیا از بازسازی اطلاعات اولیه بانک اطلاعاتی، اطمینان دارید؟", "توجه", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button2);
if (dr==DialogResult.OK)
{
if (workerMainForm.IsBusy != true)
{
WatingForm = new FrmWaitingMessageBox();
WatingForm.Opacity = 0.7;
WatingForm.Message = "سیستم در حال بازسازی بانک اطلاعاتی است. لطفاً صبر کنید";
WatingForm.Show();
workerMainForm.RunWorkerAsync();
}
else
{
MessageBoxEx.EnableGlass = false;
MessageBoxEx.Show("سیستم در حال جمع آوری اطلاعات است. لطفاً منتظر بمانید", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
return;
}
}
}


سپس در متد DowWorker به صورت زیر:


private void workerMainForm_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;


if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
else
{
RecoveryAccessTables(worker);
}
}


و متدی هم که در Doworker فراخوانی کردم به صورت زیر:


private void RecoveryAccessTables(BackgroundWorker BusyWorker)
{
if (this.InvokeRequired)
{
MethodInvoker method = delegate { RecoveryAccessTables(BusyWorker); };
this.Invoke(method);
return;
}
else
{
if (AccessControlClass.RecoverFormControlsTables())
{
WatingForm.Close();
MessageBoxEx.EnableGlass = false;
MessageBoxEx.Show("بازیابی با موفقیت انجام شد", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);

}
else
{
WatingForm.Close();
MessageBoxEx.EnableGlass = false;
MessageBoxEx.Show("خطا در بازیابی اطلاعات اولیه دیتابیس", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
}
}


این متد یه سری از تیبل های حیاتی دیتابیس رو ریست میکنه:


public static bool RecoverFormControlsTables()
{
string[] Query;
StringBuilder str = new StringBuilder();


try
{
str.Append("delete from OControlTable");
str.Append("*delete from OFormTable");
str.Append("*DBCC CHECKIDENT ('OControlTable', RESEED, 0)");
str.Append("*DBCC CHECKIDENT ('OFormTable', RESEED, 0)");
string[] Forms = GetApplicationFormNames();
string[] ControlsTag;
string[] FormTagName;
foreach (string item in Forms)
{
if (item=="FrmAccessControl")
{

}
FormTagName = new string[3];
FormTagName = GetFormTags(item);
if (short.Parse(FormTagName[0])>0)
{
str.Append(string.Format("*Insert Into OFormTable (FormName, FormText, FormTitle, FormTag) Values (N'{0}', N'{1}', N'{2}', N'{3}')", item, FormTagName[1], FormTagName[2], FormTagName[0]));
ControlsTag = GetFormsControlsTag(item);
foreach (string CnlTag in ControlsTag)
{
str.Append(string.Format("*Insert Into OControlTable (FormTag, ControlTag) Values (N'{0}', N'{1}')", FormTagName, CnlTag));
}
}
}
Query = str.ToString().Split('*');
}
catch (Exception)
{
return false;
}


connClass con = new connClass();
try
{
con.SetCommandArray(Query);
}
catch (Exception e)
{
MessageBoxEx.EnableGlass = false;
MessageBoxEx.Show("خطا در بازسازی بانک اطلاعاتی" + "\n\r\n\r" + e.Message, "ERR-AccessControlClass - 5", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
if (con.Exeptionflag)
{
return false;
}
return true;
}


و در نهایت از فرصتی که میزاری ممنونم مهندس رحیمی عزیز

ژیار رحیمی
سه شنبه 03 فروردین 1395, 19:35 عصر
سلام
به نظر من پیاده سازی درستی از backgroundworker انجام ندادی.وقتی شما آبجکتی به صورت گلوبال در سطح کلاس form تعریف کردی cast کردن پارامتر sender از ایونتworkerMainForm_DoWork لازم نیست (همچنین لازم به پاس کردن backgroundworker به تابع RecoveryAccessTables نیست)و کار اضافه یی صورت میگیرد.به مراتب روش حل رو پیچیده کردی میتونی ساده ترم نوشت.

به صورت زیر توابع رو اصلاح کن ببین به نتیجه میرسی

bool ActionDone;//برای نگهداشتن نتیجه تابع
BackgroundWorker workerMainForm = new BackgroundWorker();
void workerMainForm_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
WatingForm.Close();
MessageBoxEx.EnableGlass = false;
ActionDone?
MessageBoxEx.Show("بازیابی با موفقیت انجام شد", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
:MessageBoxEx.Show("خطا در بازیابی اطلاعات اولیه دیتابیس", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
btnDBRecovery.Enabled = true;//فعال کردن مجدد دکمه
}



void workerMainForm_DoWork(object sender, DoWorkEventArgs e)
{
Invoke((MethodInvoker)delegate
{
WatingForm = new FrmWaitingMessageBox();
WatingForm.Opacity = 0.7;
WatingForm.Message = "سیستم در حال بازسازی بانک اطلاعاتی است. لطفاً صبر کنید";
WatingForm.Show();
ActionDone = RecoverFormControlsTables();
});

}


private void btnDBRecovery_Click(object sender, EventArgs e)
{
MessageBoxEx.EnableGlass = false;
DialogResult dr = MessageBoxEx.Show("آیا از بازسازی اطلاعات اولیه بانک اطلاعاتی، اطمینان دارید؟", "توجه", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button2);
if (dr == DialogResult.OK)
{
btnDBRecovery.Enabled = false;//غیر فعال کردن دکمه تا اتمام عملیات
workerMainForm.RunWorkerAsync();

}
}

prans_tork
سه شنبه 03 فروردین 1395, 23:23 عصر
سلام
به نظر من پیاده سازی درستی از backgroundworker انجام ندادی.وقتی شما آبجکتی به صورت گلوبال در سطح کلاس form تعریف کردی cast کردن پارامتر sender از ایونتworkerMainForm_DoWork لازم نیست (همچنین لازم به پاس کردن backgroundworker به تابع RecoveryAccessTables نیست)و کار اضافه یی صورت میگیرد.به مراتب روش حل رو پیچیده کردی میتونی ساده ترم نوشت.

به صورت زیر توابع رو اصلاح کن ببین به نتیجه میرسی

bool ActionDone;//برای نگهداشتن نتیجه تابع
BackgroundWorker workerMainForm = new BackgroundWorker();
void workerMainForm_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
WatingForm.Close();
MessageBoxEx.EnableGlass = false;
ActionDone?
MessageBoxEx.Show("بازیابی با موفقیت انجام شد", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
:MessageBoxEx.Show("خطا در بازیابی اطلاعات اولیه دیتابیس", "توجه", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
btnDBRecovery.Enabled = true;//فعال کردن مجدد دکمه
}



void workerMainForm_DoWork(object sender, DoWorkEventArgs e)
{
Invoke((MethodInvoker)delegate
{
WatingForm = new FrmWaitingMessageBox();
WatingForm.Opacity = 0.7;
WatingForm.Message = "سیستم در حال بازسازی بانک اطلاعاتی است. لطفاً صبر کنید";
WatingForm.Show();
ActionDone = RecoverFormControlsTables();
});

}


private void btnDBRecovery_Click(object sender, EventArgs e)
{
MessageBoxEx.EnableGlass = false;
DialogResult dr = MessageBoxEx.Show("آیا از بازسازی اطلاعات اولیه بانک اطلاعاتی، اطمینان دارید؟", "توجه", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button2);
if (dr == DialogResult.OK)
{
btnDBRecovery.Enabled = false;//غیر فعال کردن دکمه تا اتمام عملیات
workerMainForm.RunWorkerAsync();

}
}



با تشکر از پاسخ شما دوست عزیز

در مورد cast کردن sender در داخل Doworker حق با شماست و اصلا بهش توجه نکرده بودم
در مورد پاس دادن worker به تابع RecoveryAccessTables هم حق با شماست و کار اضافه ای انجام شده
در مورد غیر فعال کردن دکمه تا اتمام کار هم دوباره حق با شماست

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

اما در فاصله ای که منتظر جواب شما بودم، اشکال اصلی هنگ کردن رو پیدا کردم
مشکل از قطعه کد زیر در ابتدای تابع RecoveryAccessTables بود که در واقع با این کلمه this ابتکار عمل رو از thread اصلی برنامه میگرفتم و به دست دومی میدادم:

if (this.InvokeRequired)
{
MethodInvoker method = delegate { RecoveryAccessTables(BusyWorker); };
this.Invoke(method);
return;
}


ممنونم از وقتی که گذاشتی مهندس رحیمی عزیز