此BackgroundWorker当前正忙,无法同时运行多个任务

发布于 2024-12-22 20:11:14 字数 1409 浏览 2 评论 0原文

我正在尝试在 WPF 应用程序中使用后台工作人员。这项繁重的任务使用 WebClient 下载一些 HTML 并从中解析一些信息。理想情况下,我希望在不锁定 UI 的情况下进行下载和解析,并在完成工作后将结果放入 UI 中。

它工作正常,但是,如果我快速提交“下载并解析”命令,我会收到错误:

此BackgroundWorker当前正忙,无法运行多个任务 同时

所以我做了一些谷歌搜索,似乎我可以启用后台工作人员的 .WorkerSupportsCancellation 属性,并且只需 .CancelAsync() 。但是,这并不能按预期工作(取消当前的下载和解析)。

我仍然收到上述错误。

这是我的代码:

//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);

//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();

//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    _backgroundWorker.CancelAsync();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

POCOClassFoo foo = new POCOClassFoo();

void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //This automagically sets the UI to the data.
    Foo.DataContext = foo;
}

void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    foo = parseanddownloadresult()!
}

I'm trying to use a Background Worker in a WPF application. The heavy lifting task uses WebClient to download some HTML and parse some info out of it. Ideally I want to do that downloading and parsing without locking the UI and placing the results in the UI once it's done working.

And it works fine, however, if I quickly submit the "download and parse" command, I get the error:

This BackgroundWorker is currently busy and cannot run multiple tasks
concurrently

So I did some Googling and it seems that I can enable the .WorkerSupportsCancellation property of the background worker and just .CancelAsync(). However, this doesn't work as expected (canceling the current download and parse).

I still get the above error.

Here's my code:

//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);

//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();

//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    _backgroundWorker.CancelAsync();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

POCOClassFoo foo = new POCOClassFoo();

void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //This automagically sets the UI to the data.
    Foo.DataContext = foo;
}

void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    foo = parseanddownloadresult()!
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

太阳男子 2024-12-29 20:11:14

调用 CancelAsync 仍会触发 RunWorkerCompleted 事件。在这种情况下,您需要通过检查 e.Cancelled 来确保尚未调用 CancelAsync。在此事件触发之前,您无法调用 RunWorkerAsync

或者,我建议您按照 Tigran 的建议进行操作,并每次创建一个新的 BackgroundWorker

此外,我建议将 _backgroundWorker_DoWork 的结果存储在 e.Result 中,然后从 _backgroundWorker_RunWorkerCompleted 中检索它们,

也许是这样的

BackgroundWorker _backgroundWorker;

private BackgroundWorker CreateBackgroundWorker()
{
    var bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.DoWork += _backgroundWorker_DoWork;
    bw.RunWorkerCompleted += new  _backgroundWorker_RunWorkerCompleted;
    return bw.
}

private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    if (_backgroundWorer != null)
    {
        _backgroundWorker.CancelAsync();
    }

    _backgroundWorker = CreateBackgroundWorker();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();

private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        //Error handling goes here.
    }
    else
    {
        if (e.Cancelled)
        {
            //handle cancels here.
        }
        {
            //This automagically sets the UI to the data.
            Foo.DataContext = (POCOClassFoo)e.Result;
        }
}

private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    e.Result = parseanddownloadresult()!
}

Calling CancelAsync will still fire the RunWorkerCompleted event. In this event, you need to make sure that CancelAsync has not been called, by checking e.Cancelled. Until this event fires, you cannot call RunWorkerAsync.

Alternatively, I would recommend you do what Tigran suggested and create a new BackgroundWorker each time.

Further more, I would recommend storing the results of_backgroundWorker_DoWork in e.Result, then retrieve them from the same in _backgroundWorker_RunWorkerCompleted

Maybe something like this

BackgroundWorker _backgroundWorker;

private BackgroundWorker CreateBackgroundWorker()
{
    var bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.DoWork += _backgroundWorker_DoWork;
    bw.RunWorkerCompleted += new  _backgroundWorker_RunWorkerCompleted;
    return bw.
}

private void LoadHtmlAndParse(string foobar)
{
    //Cancel whatever it is you're doing!
    if (_backgroundWorer != null)
    {
        _backgroundWorker.CancelAsync();
    }

    _backgroundWorker = CreateBackgroundWorker();

    //And start doing this immediately!
    _backgroundWorker.RunWorkerAsync(foobar);
}

//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();

private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        //Error handling goes here.
    }
    else
    {
        if (e.Cancelled)
        {
            //handle cancels here.
        }
        {
            //This automagically sets the UI to the data.
            Foo.DataContext = (POCOClassFoo)e.Result;
        }
}

private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //DOING THE HEAVY LIFTING HERE!
    e.Result = parseanddownloadresult()!
}
花期渐远 2024-12-29 20:11:14

问题是 CancelAsync() 做了它喜欢的事情:以异步方式取消。这意味着它不会立即停止,而是在一段时间后停止。该时间永远无法计算或预测,因此您有几个选择:

  1. 等待这个后台工作人员真正停止,通过循环等待直到IsBusy 属性变为 false

  2. 或者,我认为,更好的解决方案是启动另一个 后台工作程序,考虑到取消请求已经发送给第一个,所以很快就会或者稍后停止。在这种情况下,您需要知道来自哪个后台工作人员数据,以便处理它或不处理它,因为在第二个开始时,第一个仍然会运行并泵送来自WebService的数据。

希望这有帮助。

The thing is that CancelAsync() does what it climes: cancel in async way. That means that it will not stop immediately, but after some time. That time can never be calculated or predicted, so you have a couple of options:

  1. Wait until this backround worker stops really, by waiting in cycle until IsBusy property of it becomes false

  2. Or, I think, better solution is to start another background worker, considering that request of cancelation was already sent to the first one, so it will be soon or later stop. In this case, you need to know from which background worker data comes, in order to process it or not, cause on start of second the first one will still run and pump the data from WebService.

Hope this helps.

早茶月光 2024-12-29 20:11:14

CancelAsync 在工作线程取消并停止其工作之前返回。因此,您的 RunWorkerAsync 调用在工作线程准备就绪之前开始,并且您会收到该错误。您需要先等待工作人员准备好。

CancelAsync returns before the worker cancels and stops its work. Hence, your RunWorkerAsync call is starting before the worker is ready, and you're getting that error. You'll need to wait for the worker to be ready first.

≈。彩虹 2024-12-29 20:11:14

当我对跟踪异步操作的进度不感兴趣时​​,我倾向于只在 ThreadPool.QueueUserWorkItem 处调用 lambda,而不是实例化和设置必须检查状态的后台工作线程能够以合理的方式重用。

When I'm not interested in tracking progress of an async operation, I tend to prefer to just slap a lambda at ThreadPool.QueueUserWorkItem instead of instantiating and setting up a background worker that I have to check the state of to be able to reuse in a sane way.

夏夜暖风 2024-12-29 20:11:14

您需要在开始之前进行验证。

f( !bw.IsBusy )
    bw.RunWorkerAsync();
else
    MessageBox.Show("Can't run the bw twice!");

You need to verify before you kicks in.

f( !bw.IsBusy )
    bw.RunWorkerAsync();
else
    MessageBox.Show("Can't run the bw twice!");
久伴你 2024-12-29 20:11:14

您正在调用 CancelAsync,而无需等待后台工作人员实际取消工作。此外,您还必须有自己的取消工作的逻辑。 MSDN 上有一个很好的示例,它展示了如何做吧。基本上在您的 parseanddownloadresult() 方法中,您需要检查 CancellationPending 属性。

You are calling CancelAsync without waiting for the background worker to actually cancel the work. Also you must have your own logic for cancelling the work. There is a good example on MSDN which shows how to do it. Basically in your parseanddownloadresult() method you need to check the CancellationPending property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文