Повторное использование BackgroundWorker, отмените и дождитесь его - PullRequest
4 голосов
/ 12 августа 2011

Предположим, у вас есть текстовое поле поиска и к событию TextChanged прикреплен алгоритм поиска, который запускается с BackgroundWorker.Если в текстовом поле появляется новый символ, мне нужно отменить предыдущий поиск и запустить его снова.

Я пытался использовать события между основным потоком и bgw, начиная с этого предыдущего вопроса , но я по-прежнему получаю сообщение об ошибке «в данный момент занят и не может одновременно запускать несколько задач»

    BackgroundWorker bgw_Search = new BackgroundWorker();
    bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);

    private AutoResetEvent _resetEvent = new AutoResetEvent(false);

    private void txtSearch_TextChanged(object sender, EventArgs e)
    {
        SearchWithBgw();
    }

    private void SearchWithBgw()
    {
        // cancel previous search
        if (bgw_Search.IsBusy)
        {
            bgw_Search.CancelAsync();

            // wait for the bgw to finish, so it can be reused.
            _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
        }

        // start new search
        bgw_Search.RunWorkerAsync();   // error "cannot run multiple tasks concurrently"
    }

    void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
    {
        Search(txtSearch.Text, e);
    }

    private void Search(string aQuery, DoWorkEventArgs e)
    {
        int i = 1;            
        while (i < 3)             // simulating search processing...
        {
            Thread.Sleep(1000);                           
            i++;

            if (bgw_Search.CancellationPending)
            {
                _resetEvent.Set(); // signal that worker is done
                e.Cancel = true;
                return;
            }
        }
    }

РЕДАКТИРОВАТЬ Для отражения ответов.Не используйте BackgroundWorker повторно, создайте новый:

    private void SearchWithBgw()
    {   
        if (bgw_Search.IsBusy)
        {
            bgw_Search.CancelAsync();
            _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made

            bgw_Search = new BackgroundWorker();
            bgw_Search.WorkerSupportsCancellation = true;
            bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
        }

        bgw_Search.RunWorkerAsync();        
    }

Ответы [ 4 ]

7 голосов
/ 12 августа 2011

Когда вызов _resetEvent.WaitOne () завершается, рабочий поток фактически не выполняется. Он занят возвращением из DoWork () и ожидает возможности запустить событие RunWorkerCompleted, если оно есть. Это требует времени.

Не существует надежного способа гарантировать, что BGW завершается синхронно. Блокировка на IsBusy или ожидание запуска события RunWorkerCompleted приведет к взаимоблокировке. Если вы действительно хотите использовать только одну BGW, вам придется ставить запросы в очередь. Или просто не парься по мелочам и выделяй еще одну толстушку. Они стоят очень немного.

2 голосов
/ 12 августа 2011

Создайте новый фоновый рабочий, если старый существует.

private void SearchWithBgw()
{
    // cancel previous search
    if (bgw_Search.IsBusy)
    {
        bgw_Search.CancelAsync();

        // wait for the bgw to finish, so it can be reused.
        _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
        BackgroundWorker bgw_Search = new BackgroundWorker();
        bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);


    }

    // start new search
    bgw_Search.RunWorkerAsync();   // error "cannot run multiple tasks concurrently"
}

Также я знаю, что вы вставили поддельный код, но вы хотите убедиться, что вы установили _resetEvent, когда код также завершится нормально

1 голос
/ 12 августа 2011

Я думаю, вы должны рассмотреть вопрос об отмене фонового работника.

Если вы отменяете запросы и пользователь печатает быстрее, чем ваш сервер возвращает запросы, он не увидит подсказки, пока не закончит ввод.

В подобных интерактивных сценариях может быть лучше показать ответы, которые запаздывают с тем, что печатает пользователь. Ваш пользователь будет знать, что он может прекратить печатать, если слово, которое он имеет в виду, является вашим списком предложений.

Это также будет лучше для вашего сервера, когда он занят, потому что вместо множества отмененных запросов, которые будут стоить что-то, но которые в конечном итоге не будут отображаться, будет меньше запросов, ответ которых вы фактически используете.

Я столкнулся с похожими проблемами с (3d) приложениями рендеринга, где ошибка новичка заключается в отмене и повторном рендеринге при каждом перемещении мыши. Это привело к большому количеству вычислений и небольшому количеству интерактивных отзывов.

1 голос
/ 12 августа 2011
  • Не используйте фонового работника повторно. Это дешевый ресурс, это не тема.
  • убедитесь, что ваш код Bgw останавливается, ваш выглядит хорошо. Bgw выпустит поток в пул.
  • но в то же время создайте новую задачу / Bgw для новой работы.
  • Вы можете отменить подписку на событие «Завершено» от старого Bgw
...