BackgroundWorker有关取消的问题 [英] BackgroundWorker questions on cancellation

查看:100
本文介绍了BackgroundWorker有关取消的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试(再次)在我的应用中实现Backgroundworker,以便UI能够响应.用户选择最终要在网格上处理的文件.当处理开始时,就像这样:

I'm trying (yet again) to implement a Backgroundworker in my app so that the UI will be responsive. The user selects files to be processed which end up on a grid. When the processing starts it's like this:

for (int i = 0; i<Grid.Rows.Count; i++)
{

    Worker work = new Worker();
    //set up data for processing based on the current row in the grid
    bw.RunWorkerAsync(work);
    addStatusToGrid();
    //clean up; indicate work on this file is done
    work=null;


}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
    Worker work = (Worker)e.Argument;

    if (work.doTheWork() == false)
    {
        //indicate failure

    }
    else
    {
        //indicate success
    }
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //not sure what to do here
}

但是发生的是,一旦调用bw.RunWorkerAsync(),应用程序立即转到for循环中的下一行,然后再次启动相同的过程.现在,我有点想要这样做,但是收到一条错误消息:此背景工作人员当前很忙,无法同时运行多个任务".如果我仅处理一项,则addStatusToGrid()会立即被调用,并且由于没有完成工作,因此当然没有要添加的状态.

BUT what happens is that as soon as bw.RunWorkerAsync( ) is called the app immediately goes to the next line in the for loop and then starts the same process again. Now, I sort of want this but I get an error message, "This backgroundworker is currently busy and cannot run multiple tasks concurrently". If I just process one item, addStatusToGrid() gets called immediately and of course there is no status to add as the work is not done.

几个问题:如果我可以使用此机制一次启动多个处理会话,那将是一件好事.但是,如何防止addStatusToGrid()立即被调用?

SEVERAL QUESTIONS: If I can use this mechanism to initiate several processing sessions at once, that would be a good thing. But how do I prevent addStatusToGrid() getting called immediately?

现在,UI确实更新了很多.我有一个进度条,它经常显示更新.我猜不能用取消按钮来取消操作,因为UI线程很忙.

Right now, the UI does get updated a lot. I have a progress bar and it often shows updates. I cannot cancel the operations with a cancel button I guess because the UI thread is busy.

推荐答案

从您的问题看来,您在网格中有多个行需要处理.现在,后台工作人员在另一个线程中执行该方法,从而释放了UI来执行其他操作. (IE状态更新).由于这是在另一个线程上执行的,因此UI可以自由继续正常进行处理. UI线程将不等待BackgroundWorker完成.如果确实等待,则使用后台工作程序将毫无用处.另外,BackgroundWorker必须在开始另一项操作之前完成该操作(因此会出现错误消息).

From your question it appears that you have multiple rows in the grid that need to be processed. Now the background worker executes the method in another thread thus freeing up the UI to perform additional actions. (IE status updates). As this is executed on another thread the UI is free to continue processing as normal. The UI thread will not wait for the BackgroundWorker to finish. If it did wait, there would be no use using the background worker. Additionally the BackgroundWorker must finish the operation before starting another operation (thus your error message).

现在很难理解您在work.doTheWork()中的工作,但是我怀疑您想在这里采取另一种方法.

Now it is hard to understand what you are doing in the work.doTheWork() but I suspect you would want to take another approach here.

在开始解决该问题之前,您必须首先了解的是,取消背景工作者实际上并没有取消线程,而是为您的代码提供了一个标志",以检查BackgroundWorker是否正在等待计算(此标志上的MSDN ).

Before we begin tackling the question the first thing you must understand is the Cancellation of a background worker doesn't actually cancel the thread but provides a "flag" for your code to check if the BackgroundWorker is pending calculation (MSDN on this flag ).

前进.由于网格位于UI线程上,因此您需要了解我们要捕获的网格中的数据并将其传递到BackgroundWorker上下文中.正如该研究所提供的那样,我将做一个非常基本的示例.现在,您可以运行多个后台工作程序来单独处理每一行,从而在网格中每行产生1个工作程序.对于您的需求而言,这可能是理想的选择,但对于较大的网格,您实际上是在每行创建1个线程来处理,而在具有数百行的网格中,这可能是灾难性的.

Moving forward. As the Grid is held on the UI thread you need to understand the data from the grid that we want to capture and pass it into the BackgroundWorker context. As this inst provided I am going to do a very basic example. Now you could run multiple background workers to process each row individually thus spawning 1 worker per row in the Grid. For your requirement this may be ideal but for larger grids you are essentially creating 1 thread per row to process and in a grid with hundreds of rows this could be disastrous.

因此,您可以创建类似下面的方法.我已对代码进行了注释,以帮助您运行它.基本上,这表明可以取消工作程序,报告进度并分别遍历每个行项目.我还添加了一些在worker内部使用的基本类. [请注意,这仅是演示代码]

So to being you can create a method something like below. I have commented the code to help you run through it. Bascially this shows the ability to cancel the worker, report progress and iterate through each row item individually. I also added some basic classes for use inside the worker. [Note this is demo code only]

class DoWorkTester
{
    int currentIndex = 0;
    GridRow[] rows;


    public void ExecuteWorkers()
    {

        GridRow rowA = new GridRow
        {
            PropertyA = "abc",
            PropertyB = "def"
        };

        GridRow rowB = new GridRow
        {
            PropertyA = "123",
            PropertyB = "456"
        };

        GridRow rowC = new GridRow
        {
            PropertyA = "xyz",
            PropertyB = "789"
        };

        rows = new GridRow[] { rowA, rowB, rowC };

        currentIndex = 0;

        runWorkers();
    }

    BackgroundWorker worker;

    void runWorkers()
    {
        //done all rows
        if (currentIndex >= rows.Length - 1)
            return;

        //is the worker busy?
        if (worker != null && worker.IsBusy)
        {
            //TODO: Trying to execute on a running worker.
            return;
        }

        //create a new worker
        worker = new BackgroundWorker();
        worker.WorkerSupportsCancellation = true;
        worker.WorkerReportsProgress = true;
        worker.ProgressChanged += (o, e) =>
        {
            //TODO: Update the UI that the progress has changed
        };
        worker.DoWork += (o, e) =>
        {
            if (currentIndex >= rows.Length - 1)
            {
                //indicate done
                e.Result = new WorkResult
                {
                    Message = "",
                    Status = WorkerResultStatus.DONE
                };
                return;
            }
            //check if the worker is cancelling
            else if (worker.CancellationPending)
            {
                e.Result = WorkResult.Cancelled;
                return;
            }
            currentIndex++;

            //report the progress to the UI thread.
            worker.ReportProgress(currentIndex);

            //TODO: Execute your logic.
            if (worker.CancellationPending)
            {
                e.Result = WorkResult.Cancelled;
                return;
            }

            e.Result = WorkResult.Completed;
        };
        worker.RunWorkerCompleted += (o, e) =>
        {
            var result = e.Result as WorkResult;
            if (result == null || result.Status != WorkerResultStatus.DONE)
            {
                //TODO: Code for cancelled  \ failed results
                worker.Dispose();
                worker = null;
                return;
            }
            //Re-call the run workers thread
            runWorkers();
        };
        worker.RunWorkerAsync(rows[currentIndex]);
    }

    /// <summary>
    /// cancel the worker
    /// </summary>
    void cancelWorker()
    {
        //is the worker set to an instance and is it busy?
        if (worker != null && worker.IsBusy)
            worker.CancelAsync();

    }
}

enum WorkerResultStatus
{
    DONE,
    CANCELLED,
    FAILED
}

class WorkResult
{
    public string Message { get; set; }
    public WorkerResultStatus Status { get; set; }

    public static WorkResult Completed
    {
        get
        {
            return new WorkResult
            {
                Status = WorkerResultStatus.DONE,
                Message = ""
            };
        }
    }

    public static WorkResult Cancelled
    {
        get
        {
            return new WorkResult
            {
                Message = "Cancelled by user",
                Status = WorkerResultStatus.CANCELLED
            };
        }
    }
}


class GridRow
{
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
}

现在,如果您想一次处理多行,则必须调整代码以使用某种类型的工作池,或将所有行数据传递给第一个后台工作器,从而消除递归.

Now if you wanted to process multiple rows at a time you will have to adapt the code to use some sort of Worker Pooling or pass all the row data to the first background worker thus removing the recursion.

干杯.

这篇关于BackgroundWorker有关取消的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆