如何在发生异常时暂停后台工作线程,并在发生异常时恢复它
本文关键字:异常 线程 恢复 暂停 后台 工作 | 更新日期: 2023-09-27 18:30:31
im 使用以下代码向数据库中的所有学生发送短信
private void btnsend_Click(object sender, EventArgs e)
{
foreach (Control c in Controls)
{
c.Enabled = false;
}
if (!bgw.IsBusy)
{
bgw.RunWorkerAsync();
}
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
Invoke((MethodInvoker)delegate()
{
using (var sp = new SerialPort(cbcomport.Text))
{
sp.Open();
sp.WriteLine("AT" + Environment.NewLine);
sp.WriteLine("AT+CMGF=1" + Environment.NewLine);
sp.WriteLine("AT+CMGS='"" + dt.Rows[i]["PhoneNo"] + "'"" + Environment.NewLine);
sp.WriteLine(tbsms.Text + (char)26);
if (sp.BytesToRead > 0)
{
tbsentto.Text = i + 1 + " of " + dt.Rows.Count;
}
}
});
Thread.Sleep(5000);
}
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (Control c in Controls)
{
c.Enabled = true;
}
}
我的问题是:如果发生异常,则会向学生发送短信,例如端口"COM5"不存在。 我想用按钮重试和取消向用户显示相同的系统消息。 如果已解决问题。 即插入设备用户按重试按钮我想从同一点恢复线程,如果用户按下取消按钮,我想停止暂停线。
试试这个
private void btnsend_Click(object sender, EventArgs e)
{
foreach (Control c in Controls)
{
c.Enabled = false;
}
if (!bgw.IsBusy)
{
bgw.RunWorkerAsync();
}
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
if ((bgw.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
Invoke((MethodInvoker)delegate()
{
using (var sp = new SerialPort(cbcomport.Text))
{
try
{
sp.Open();
sp.WriteLine("AT" + Environment.NewLine);
sp.WriteLine("AT+CMGF=1" + Environment.NewLine);
sp.WriteLine("AT+CMGS='"" + dt.Rows[i]["PhoneNo"] + "'"" + Environment.NewLine);
sp.WriteLine(tbsms.Text + (char)26);
if (sp.BytesToRead > 0)
{
tbsentto.Text = i + 1 + " of " + dt.Rows.Count;
}
Thread.Sleep(5000);
}
catch (Exception ex)
{
if (MessageBox.Show(ex.Message, "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == System.Windows.Forms.DialogResult.Retry)
{
try
{
sp.Open();
sp.WriteLine("AT" + Environment.NewLine);
sp.WriteLine("AT+CMGF=1" + Environment.NewLine);
sp.WriteLine("AT+CMGS='"" + dt.Rows[i]["PhoneNo"] + "'"" + Environment.NewLine);
sp.WriteLine(tbsms.Text + (char)26);
if (sp.BytesToRead > 0)
{
tbsentto.Text = i + 1 + " of " + dt.Rows.Count;
}
Thread.Sleep(5000);
}
catch (Exception ex2)
{
MessageBox.Show(ex2.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
bgw.CancelAsync();
}
}
else
{
bgw.CancelAsync();
}
}
}
});
}
}
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (Control c in Controls)
{
c.Enabled = true;
}
}
对于这样的方案,一般的想法是你需要:
- 捕获
- 可能从worker抛出的异常(但仅限于您知道可以处理的异常;捕获所有异常是邪恶的!
- 通知 UI 线程提示用户
- 暂停执行,直到 UI 线程收到输入
- 如果输入指示,则中止执行
对于最后两项,请使用可等待的对象,例如 AutoResetEvent
和状态变量(即使是 bool
至少也可以),以指示是否应中止任务。这些变量必须可从 UI 线程和辅助角色访问:
AutoResetEvent pauseEvent(false);
bool shouldAbort;
然后,工作线程的代码变为:
for (int i = 0; i < dt.Rows.Count; i++)
{
try
{
// your existing code goes here
}
catch (SomeException ex) // do not catch all exceptions!
{
BeginInvoke(...); // tell the UI thread something bad happened
pauseEvent.WaitOne(); // and block the worker until user gives input
if (shouldAbort)
{
// cleanup any other resources if required and then
break;
}
}
}
在BeginInvoke
行中,应调用某个向用户显示相应消息并请求说明的方法。 BeginInvoke
将立即返回,工作人员将通过拨打pauseEvent.WaitOne
无限期地阻止AutoResetEvent
。
UI 方法应查询用户,并在收到响应后发出信号pauseEvent
pauseEvent.Set();
这将取消阻止工作线程并允许其恢复执行。由于代码的结构,这意味着它将继续循环的下一次迭代。如果要改为使工作线程中止,请在发出pauseEvent
信号之前将shouldAbort
设置为 true
。
如果减少catch
块内的i
,还可以使代码重试引发异常的迭代(而不是跳过它并继续下一个)。
你必须在
线程之间使用sync
对象。特别是在wait handlers
部分查看此处。