Backgroundworker 取消worker
Backgroundworker cancel the worker
我在尝试取消 Backgroundworker 时遇到了一些麻烦。
我已经阅读了数十个 os 类似的主题,例如 How to stop BackgroundWorker correctly, How to wait correctly until BackgroundWorker completes?,但我没有到达任何地方。
我有一个 C# 应用程序,它使用 PHP WebService 将信息发送到 MySQL 数据库。如果用户出于某种原因(在表单中)单击 "back" 或 "stop" 按钮,则会触发以下代码:
BgWorkDocuments.CancelAsync();
BgWorkArticles.CancelAsync();
我知道请求是 Asynchronous
,因此取消可能需要 1 或 2 秒,但它应该停止..而这根本不会发生。即使在单击 "back" 之后(当前表单是 closed 并且打开了一个新表单),后台工作人员仍在继续工作,因为我一直看到数据被插入到 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"]);
}
}
};
}
我同意对代码甚至可以工作表示惊讶的评论,因为调用 RunWorkerAsync()
与实际订阅 DoWork
事件时的顺序问题明显。此外,您对 DoEvents()
的使用是没有根据的,应该删除(就像 any 使用 DoEvents()
的情况一样)。
我还注意到,当您尝试取消时,您的工作人员实际上并没有 退出。您只是跳过处理,但继续在行上循环。如果没有看到其余代码,就不可能知道发生了什么,但是 可能 在您取消后, CancellationPending
属性 会重置为 false
,允许循环再次开始做事。
缺少完整的代码示例是理解正在发生的事情的全部细节的真正障碍。
就是说,恕我直言,这似乎根本不是您实际需要 BackgroundWorker
的情况,C# 中新的 async
/await
功能不需要。鉴于涉及到网络 I/O,我的猜测是每次调用 sendDocumentsToMySQL()
和 sendArticlesToMySQL()
都可以在线程池中单独执行,而不会产生太多开销(或者甚至可以写成作为 async 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"]));
}
}
我在尝试取消 Backgroundworker 时遇到了一些麻烦。 我已经阅读了数十个 os 类似的主题,例如 How to stop BackgroundWorker correctly, How to wait correctly until BackgroundWorker completes?,但我没有到达任何地方。
我有一个 C# 应用程序,它使用 PHP WebService 将信息发送到 MySQL 数据库。如果用户出于某种原因(在表单中)单击 "back" 或 "stop" 按钮,则会触发以下代码:
BgWorkDocuments.CancelAsync();
BgWorkArticles.CancelAsync();
我知道请求是 Asynchronous
,因此取消可能需要 1 或 2 秒,但它应该停止..而这根本不会发生。即使在单击 "back" 之后(当前表单是 closed 并且打开了一个新表单),后台工作人员仍在继续工作,因为我一直看到数据被插入到 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"]);
}
}
};
}
我同意对代码甚至可以工作表示惊讶的评论,因为调用 RunWorkerAsync()
与实际订阅 DoWork
事件时的顺序问题明显。此外,您对 DoEvents()
的使用是没有根据的,应该删除(就像 any 使用 DoEvents()
的情况一样)。
我还注意到,当您尝试取消时,您的工作人员实际上并没有 退出。您只是跳过处理,但继续在行上循环。如果没有看到其余代码,就不可能知道发生了什么,但是 可能 在您取消后, CancellationPending
属性 会重置为 false
,允许循环再次开始做事。
缺少完整的代码示例是理解正在发生的事情的全部细节的真正障碍。
就是说,恕我直言,这似乎根本不是您实际需要 BackgroundWorker
的情况,C# 中新的 async
/await
功能不需要。鉴于涉及到网络 I/O,我的猜测是每次调用 sendDocumentsToMySQL()
和 sendArticlesToMySQL()
都可以在线程池中单独执行,而不会产生太多开销(或者甚至可以写成作为 async 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"]));
}
}