3

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

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);
    }
}

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

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

4

4 回答 4

4

现在我看到您已将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();
}

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

于 2013-11-13T09:26:58.200 回答
1

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

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。

于 2013-11-13T09:35:24.630 回答
0

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

这是如何正确停止后台工作人员的响应元素

于 2013-11-13T09:25:34.800 回答
0

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

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.IsBusycalendar.IsEnabled让它“自动”处理(同时仍然不必担心工作方法的并发执行)。

于 2013-11-13T09:51:34.033 回答