我在尝试取消Backgroundworker时遇到了一些麻烦。 我已经阅读了几十个类似于os的类似主题,例如How to stop BackgroundWorker correctly,How to wait correctly until BackgroundWorker completes?,但我没有到达任何地方。
发生的事情是我有一个C#应用程序,它使用PHP WebService将信息发送到MySQL数据库。如果用户由于某种原因在“后退”或“停止”按钮上单击(在表单中),则会触发代码:
BgWorkDocuments.CancelAsync();
BgWorkArticles.CancelAsync();
我确实理解请求是Asynchronous
,因此取消可能需要1或2秒,但它应该停止......而这根本不会发生。即使点击“返回”(当前表单已关闭并打开一个新表单),后台工作人员仍然继续工作,因为我一直看到数据被插入到MySQL中。
foreach (string[] conn in lines)
{
string connectionString = conn[0];
FbConnection fbConn = new FbConnection(connectionString);
fbConn.Open();
getDocuments(fbConn);
// Checks if one of the backgrounds is currently busy
// If it is, then keep pushing the events until stop.
// Only after everything is completed is when it's allowed to close the connection.
//
// OBS: Might the problem be here?
while (BgWorkDocuments.IsBusy == true || BgWorkArticles.IsBusy == true)
{
Application.DoEvents();
}
fbConn.Close();
}
上面的代码是必需的,因为我可能有多个数据库,这就是我有循环的原因。
private void getDocuments(FbConnection fbConn)
{
BgWorkDocuments.RunWorkerAsync();
BgWorkDocuments.DoWork += (object _sender, DoWorkEventArgs args) =>
{
DataTable dt = getNewDocuments(fbConn);
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Checks if the user has stopped the background worker
if (BgWorkDocuments.CancellationPending == false)
{
// Continue doing what has to do..
sendDocumentsToMySQL((int)dt.Rows[i]["ID"]);
}
}
// After the previous loop is completed,
// start the new backgroundworker
getArticles(fbConn);
};
}
private void getArticles(FbConnection fbConn)
{
BgWorkArticles.RunWorkerAsync();
BgWorkArticles.DoWork += (object _sender, DoWorkEventArgs args) =>
{
DataTable dt = getNewArticles(fbConn);
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Checks if the user has stopped the background worker
if (BgWorkArticles.CancellationPending == false)
{
// Continue doing what has to do..
sendArticlesToMySQL((int)dt.Rows[i]["ID"]);
}
}
};
}
答案 0 :(得分:0)
我同意这些评论表达意外的意见,因为当您实际订阅RunWorkerAsync()
事件时调用DoWork
vs的明显排序问题。此外,您对DoEvents()
的使用是没有根据的,应予以删除(与任何使用DoEvents()
一样)。
我还注意到,当您尝试取消它们时,您的工作人员实际上并未退出。您只是跳过处理,但继续循环行。在没有看到其余代码的情况下,不可能知道发生了什么,但是可能在取消后,CancellationPending
属性被重置为false
,允许循环再次开始做事。
缺乏完整的代码示例是理解正在发生的事情的全部细节的真正障碍。
那就是说,恕我直言,这似乎不是你真正需要BackgroundWorker
的情况,而不是C#中新的async
/ await
功能。鉴于涉及网络I / O,我的猜测是每个对sendDocumentsToMySQL()
和sendArticlesToMySQL()
的调用都可以在线程池中单独执行而不需要太多开销(或者甚至可以写为异步) I / O方法......再次,缺乏关于其具体实现的细节,阻止了在这方面的任何具体建议)。鉴于此,您的代码可能会被重写,因此它看起来更像是这样:
private CancellationTokenSource _cancelSource;
private void stopButton_Click(object sender, EventArgs e)
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}
private async void startButton_Click(object sender, EventArgs e)
{
using (CancellationTokenSource cancelSource = new CancellationTokenSource)
{
_cancelSource = cancelSource;
try
{
foreach (string[] conn in lines)
{
string connectionString = conn[0];
FbConnection fbConn = new FbConnection(connectionString);
fbConn.Open();
try
{
await getDocuments(fbConn, cancelSource.Token);
await getArticles(fbConn, cancelSource.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
fbConn.Close();
}
}
}
finally
{
_cancelSource = null;
}
}
}
private async Task getDocuments(FbConnection fbConn, CancellationToken cancelToken)
{
DataTable dt = await Task.Run(() => getNewDocuments(fbConn));
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
cancelToken.ThrowIfCancellationRequested();
await Task.Run(() => sendDocumentsToMySQL((int)dt.Rows[i]["ID"]));
}
}
private async Task getArticles(FbConnection fbConn, CancellationToken cancelToken)
{
DataTable dt = await Task.Run(() => getNewArticles(fbConn));
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
cancelToken.ThrowIfCancellationRequested();
await Task.Run(() => sendArticlesToMySQL((int)dt.Rows[i]["ID"]));
}
}