Backgroundworker取消了工人

时间:2015-01-23 16:32:58

标签: c# .net backgroundworker

我在尝试取消Backgroundworker时遇到了一些麻烦。 我已经阅读了几十个类似于os的类似主题,例如How to stop BackgroundWorker correctlyHow 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"]);
            }
        }

    }; 
}

1 个答案:

答案 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"]));
    }
}