【问题标题】:Backgroundworker cancel the worker后台工作者取消工作者
【发布时间】:2015-01-24 03:59:18
【问题描述】:

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

    }; 
}

【问题讨论】:

  • 无论第一个线程发生什么,您都会进入第二个线程。所以在getDocuments() 期间取消不会阻止getArticles() 触发。在getDocument() 通话中需要return 吗?
  • 然后失去DoEvents() 电话......这是个坏主意。如果您正在寻找另一种刷新方式。
  • 我同意你的观点,我已经将验证是否取消的代码更改为:pastebin.com/3uEfNZqa,同样的代码(仅更改后台工作人员名称)也应用于 getArticles() ..但问题仍然存在。
  • 查看此模式。 msdn.microsoft.com/en-us/library/…需要传递发件人,取消发件人
  • 你在 dowork 事件被附加之前就开始工作了,这是怎么回事?

标签: c# .net backgroundworker


【解决方案1】:

我同意 cmets 对代码甚至可以正常工作表示惊讶,因为调用 RunWorkerAsync() 与您实际订阅 DoWork 事件时存在明显的顺序问题。此外,您对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"]));
    }
}

【讨论】:

  • 我会接受你的回答,因为我知道它是正确的,但我的目标是 .NET Framework 3.5 而Async 是针对 .NET Framework 4.5。因此,我实现了Threads 而不是Backgroundworkers,我已经成功解决了我的问题。
猜你喜欢
  • 1970-01-01
  • 2011-07-29
  • 1970-01-01
  • 2011-10-30
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多