2015-01-23 3 views
0

У меня возникли некоторые проблемы при попытке отменить Фоноверто. Я читал десятки аналогичных тем, таких как How to stop BackgroundWorker correctly, How to wait correctly until BackgroundWorker completes?, но я никуда не денусь.Фоновый рабочий отменить рабочего

Что происходит, так это то, что у меня есть приложение C#, которое использует PHP WebService для отправки информации в базу данных MySQL. Если пользователь по какой-то причине, щелчки (в форме) на «назад» или «стоп» кнопку, это код, который обжигают:

BgWorkDocuments.CancelAsync(); 
BgWorkArticles.CancelAsync(); 

Я понимаю, что запрос Asynchronous, поэтому отмена может занять 1 или 2 секунды, но она должна прекратиться ... и этого не происходит вообще. Даже после нажатия «назад» (текущая форма закрыта и открывается новая), рабочий стол продолжает работать, потому что я все время вижу данные, вставленные в MySQL.

foreach (string[] conn in lines) 
{ 

    string connectionString = conn[0]; 

    FbConnection fbConn = new FbConnection(connectionString); 
    fbConn.Open(); 

    getDocuments(fbConn); 

    // Checks if one of the backgrounds is currently busy 
    // If it is, then keep pushing the events until stop. 
    // Only after everything is completed is when it's allowed to close the connection. 
    // 
    // OBS: Might the problem be here? 
    while (BgWorkDocuments.IsBusy == true || BgWorkArticles.IsBusy == true) 
    { 
     Application.DoEvents(); 
    } 

    fbConn.Close(); 
} 

Этот код необходим, потому что у меня может быть несколько баз данных, поэтому у меня есть цикл.

private void getDocuments(FbConnection fbConn) 
{ 
    BgWorkDocuments.RunWorkerAsync(); 

    BgWorkDocuments.DoWork += (object _sender, DoWorkEventArgs args) => 
    { 

     DataTable dt = getNewDocuments(fbConn); 

     for (int i = 0; i <= dt.Rows.Count - 1; i++) 
     { 

      // Checks if the user has stopped the background worker 
      if (BgWorkDocuments.CancellationPending == false) 
      { 
       // Continue doing what has to do.. 
       sendDocumentsToMySQL((int)dt.Rows[i]["ID"]); 
      } 
     } 

     // After the previous loop is completed, 
     // start the new backgroundworker 

     getArticles(fbConn); 

    }; 
} 

private void getArticles(FbConnection fbConn) 
{ 
    BgWorkArticles.RunWorkerAsync(); 

    BgWorkArticles.DoWork += (object _sender, DoWorkEventArgs args) => 
    { 

     DataTable dt = getNewArticles(fbConn); 

     for (int i = 0; i <= dt.Rows.Count - 1; i++) 
     { 

      // Checks if the user has stopped the background worker 
      if (BgWorkArticles.CancellationPending == false) 
      { 
       // Continue doing what has to do.. 
       sendArticlesToMySQL((int)dt.Rows[i]["ID"]); 
      } 
     } 

    }; 
} 
+0

Вы запускаете второй поток независимо от того, что происходит во время первого. Таким образом, отмена во время 'getDocuments()' не будет препятствовать запуску 'getArticles()'. Вам нужно 'return' в вызове' getDocument() '? – DonBoitnott

+1

И потерять вызов DoEvents() ... это плохая идея. Найдите другой способ обновления, если это то, что вам нужно. – DonBoitnott

+0

Я согласен с вами, и я изменил код, который проверяет, отменено ли это: http://pastebin.com/3uEfNZqa, тот же код (изменяющий только имя фонового рабочего) был применен и к getArticles(). но проблема сохраняется. – Linesofcode

ответ

0

Я согласен с замечаниями, выразив удивление, что код работает даже из-за очевидную проблему упорядочения обращения к RunWorkerAsync() против, когда вы на самом деле подписаться на DoWork событие. Кроме того, ваше использование DoEvents() является необоснованным и должно быть удалено (как в случае с любым использованием DoEvents()).

Я также отмечаю, что ваши работники фактически не выход, когда вы пытаетесь их отменить. Вы просто пропустите обработку, но продолжайте цикл в строках. Не видя остальной части кода, невозможно узнать, что происходит, но возможно , возможно,, что после того, как вы отмените, свойство CancellationPending будет сброшено до false, что позволит петлям снова начать делать что-то еще.

Отсутствие полного примера кода является реальным препятствием для понимания полной информации о том, что происходит.

Это говорит, ИМХО это, кажется, не тот случай, когда вы на самом деле нужно BackgroundWorker вообще, а не с новой функцией async/await в C#. Учитывая, что задействован сетевой ввод-вывод, я предполагаю, что каждый вызов sendDocumentsToMySQL() и sendArticlesToMySQL() может выполняться индивидуально в пуле потоков без слишком больших издержек (или даже может быть написан как методы асинхронного ввода-вывода & hellip; подробная информация об их конкретной реализации не дает каких-либо конкретных рекомендаций в этом отношении). Учитывая, что ваш код, вероятно, был бы переписан так, чтобы он выглядел примерно так:

private CancellationTokenSource _cancelSource; 

private void stopButton_Click(object sender, EventArgs e) 
{ 
    if (_cancelSource != null) 
    { 
     _cancelSource.Cancel(); 
    } 
} 

private async void startButton_Click(object sender, EventArgs e) 
{ 
    using (CancellationTokenSource cancelSource = new CancellationTokenSource) 
    { 
     _cancelSource = cancelSource; 

     try 
     { 
      foreach (string[] conn in lines) 
      { 
       string connectionString = conn[0]; 

       FbConnection fbConn = new FbConnection(connectionString); 
       fbConn.Open(); 

       try 
       { 
        await getDocuments(fbConn, cancelSource.Token); 
        await getArticles(fbConn, cancelSource.Token); 
       } 
       catch (OperationCanceledException) 
       { 
        return; 
       } 
       finally 
       { 
        fbConn.Close(); 
       } 
      } 
     } 
     finally 
     { 
      _cancelSource = null; 
     } 
    } 
} 

private async Task getDocuments(FbConnection fbConn, CancellationToken cancelToken) 
{ 
    DataTable dt = await Task.Run(() => getNewDocuments(fbConn)); 

    for (int i = 0; i <= dt.Rows.Count - 1; i++) 
    { 
     cancelToken.ThrowIfCancellationRequested(); 

     await Task.Run(() => sendDocumentsToMySQL((int)dt.Rows[i]["ID"])); 
    } 
} 

private async Task getArticles(FbConnection fbConn, CancellationToken cancelToken) 
{ 
    DataTable dt = await Task.Run(() => getNewArticles(fbConn)); 

    for (int i = 0; i <= dt.Rows.Count - 1; i++) 
    { 
     cancelToken.ThrowIfCancellationRequested(); 

     await Task.Run(() => sendArticlesToMySQL((int)dt.Rows[i]["ID"])); 
    } 
} 
+0

Я приму свой ответ, потому что знаю, что это правильно, но я нацелен на .NET Framework 3.5, а «Async» - на .NET Framework 4.5. Поэтому я реализовал «Threads» вместо «Backgroundworkers», и я успешно решил свою проблему. – Linesofcode

Смежные вопросы