【问题标题】:Stop running backgroundworker and start new one.停止运行 backgroundworker 并启动新的。
【发布时间】:2013-11-13 17:51:34
【问题描述】:

我有一个带有日历和数据网格的窗口。
当用户在日历中选择一个新日期时,我想查询数据库以获取在该日期进行的调用。

public HistoryDialog()
{
    InitializeComponent();

    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

    HistoryGrid.SelectionChanged += new SelectionChangedEventHandler(HistoryGrid_SelectionChanged);
}

void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
    startDate = calendar.SelectedDates.OrderBy(x => x.Date).FirstOrDefault();
    endDate = calendar.SelectedDates.OrderByDescending(x => x.Date).FirstOrDefault();

    if (endDate != startDate)
        SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate) + " - " + String.Format("{0:d MMMM}", endDate);
    else
        SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate);

    SearchInDatabase();
}

private void SearchInDatabase()
{
    if (worker.IsBusy)
        worker.CancelAsync();

    worker.RunWorkerAsync();
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    if ((worker.CancellationPending == true))
    {
        e.Cancel = true;
        return;
    }

    var CallLog = ct.GetCalllogs(startDate, endDate, userID);   // Database query
    e.Result = CallLog;
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    IList CallLog = e.Result as IList;

    foreach (CalllogInfo callInfo in CallLog)
    {
        chvm.CallHistoryList.Add(callInfo);
    }
}

但是当用户在后台工作程序仍在运行时选择新日期时,我的程序崩溃了。

如何停止正在运行的后台工作程序并启动一个新工作程序?

【问题讨论】:

  • 程序崩溃时出现什么错误?
  • 说后台worker还在忙,不能执行多个任务。

标签: c# wpf backgroundworker


【解决方案1】:

现在我看到您已将 WorkerSupportsCancellation 属性设置为 true。

现在这实际上并没有取消您的 BackgroundWorker,它只是允许您调用 CancelAsync() 方法。

您需要做的是在您的方法处理过程中定期检查以确保工作没有等待从CancellationPending 属性取消。当您检查此属性时,您可以将事件参数的 Cancel 属性设置为 true。这将在您的RunWorkerCompleted 活动中提供。此时(在您的RunWorkerCompleted 事件处理程序中)您可以重新启动BackgroundWorker

这是一个使用非常基本的后台工作者的示例,它支持取消并响应取消请求。

public MainWindow()
{
    InitializeComponent();
    this.DataContext = dataModel;

    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += (o, e) =>
    {
        //do a long running task
        for (int i = 0; i < 10; i++)
        {
            System.Threading.Thread.Sleep(500);
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

    };
    worker.RunWorkerCompleted += (o, e) =>
    {
        if (e != null && e.Cancelled)
        {
            startTheWorker();
            return;
        }
        //TODO: I AM DONE!
    };
}

BackgroundWorker worker;

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (worker != null && worker.IsBusy)
        worker.CancelAsync();
    else if(worker != null && !worker.CancellationPending)
        startTheWorker();
}

void startTheWorker()
{
    if (worker == null)
        throw new Exception("How come this is null?");
    worker.RunWorkerAsync();
}

如您所见,我们必须完成实际取消工作人员的所有工作。希望这会有所帮助。

【讨论】:

    【解决方案2】:

    除了其他答案,我想添加以下内容。以下代码块负责引发错误:

    private void SearchInDatabase()
    {
        if (worker.IsBusy)
            worker.CancelAsync();
    
        worker.RunWorkerAsync();
    }
    

    当您调用 CancelAsync 时,您的代码会立即继续执行,而无需等到 BackgroundWorker 真正停止。这就是为什么在 BackgroundWorker 仍在运行时调用 RunWorkerAsync 导致您描述的错误的原因。您应该按如下方式更改代码:

    private void SearchInDatabase()
    {
        if (worker.IsBusy)
            worker.CancelAsync();
        while(worker.IsBusy)
            System.Threading.Thread.Sleep(100);
    
        worker.RunWorkerAsync();
    }
    

    这可确保 BackgroundWorker 在开始新的工作之前完成其工作。此外,您需要增强 DoWork 方法以更频繁地检查 CancellationPending 属性,以便在取消请求后立即真正停止 BackgroundWorker。

    【讨论】:

      【解决方案3】:

      您的问题来自取消。 当您在ct.GetCalllogs 时,您的代码被阻止并且不支持取消。 在ct.GetCalllogs 中,您应该验证后台工作人员未处于取消状态,然后使用新值重新开始。

      这是回复how to stop backgroundworker correctly的元素

      【讨论】:

        【解决方案4】:

        在工作人员工作时禁止选择将消除问题而无需解决它:

        void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            // .. cut ..
            // disable the calendar and go on
            calendar.IsEnabled = false;
            SearchInDatabase();
        }
        
        private void SearchInDatabase()
        {
            // No longer needed: selection processing is now "atomic"
            // if (worker.IsBusy) worker.CancelAsync();
            worker.RunWorkerAsync();
        }
        
        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // .. cut ..
            calendar.IsEnabled = true;        
        }
        

        或者您可以将 worker.IsBusy 绑定到 calendar.IsEnabled 以“自动”处理它(同时仍然不必担心工作方法的并发执行)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多