2015-01-23 3 views
0

Backgroundworker를 취소하려고 할 때 몇 가지 문제가 있습니다. How to stop BackgroundWorker correctly, How to wait correctly until BackgroundWorker completes?과 같은 수십 가지의 유사한 주제를 읽었지 만 어느 곳에도 접근하지 못했습니다.backgroundworker가 작업자를 취소합니다.

나는 PHP 웹 서비스를 사용하여 MySQL 데이터베이스에 정보를 보내는 C# 응용 프로그램을 가지고 있습니다.

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

내가 요청이 Asynchronous 것을 이해한다 그러므로 : 사용자가 어떤 이유로, 버튼을 "중지" "뒤로"에 (형태) 클릭하거나 경우이 해고되는 코드입니다 취소에는 1 ~ 2 초가 걸릴 수 있지만 멈추어야합니다. 전혀 발생하지 않습니다. "back"을 클릭 한 후에도 (현재 양식이 닫히고 새로운 양식이 열림) MySQL에 데이터가 계속 삽입되어 있기 때문에 backgroundworker가 계속 작동합니다.

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()가 실행되지 않습니다. getDocument() 호출에서'return'이 필요합니까? – DonBoitnott

+1

그리고'DoEvents()'호출을 잃어 버리는 것은 나쁜 생각입니다. 그것이 당신이 쫓고있는 것이라면 새로 고침하는 또 다른 방법을 찾아보십시오. – DonBoitnott

+0

나는 당신의 의견에 동의하고 이것을 취소했는지 확인하는 코드를 변경했다 : http://pastebin.com/3uEfNZqa, 동일한 코드 (backgroundworker 이름 만 변경)가 getArticles()에도 적용되었다. 그러나 문제는 계속됩니다. – Linesofcode

답변

0

나는 코멘트 코드도 때문에 실제로 DoWork 이벤트에 가입 할 때 대 RunWorkerAsync()에 대한 호출의 명백한 주문 문제로 작동 놀라움을 표현에 동의합니다. 또한 DoEvents()의 사용은 보증되지 않으며 제거해야합니다 (의 경우의 사용은 DoEvents()입니다).

취소를 시도하면 직원이 실제로 을 퇴사하지 않습니다. 처리를 건너 뛰고 행을 반복합니다. 코드의 나머지 부분을 보지 않고도 상황을 알 수는 없지만 이 가능합니다. 취소 한 후 CancellationPending 속성은 false으로 재설정되어 루프가 다시 작업을 시작할 수 있습니다.

완전한 코드 예제가 없기 때문에 상황에 대한 자세한 내용을 이해할 수 없습니다.

즉, IMHO는 실제로 BackgroundWorker이 필요하지는 않지만 C#의 새로운 async/await 기능이 아닌 것 같습니다. 네트워크 I/O가 관련되어 있다면, sendDocumentsToMySQL()sendArticlesToMySQL()에 대한 각각의 호출은 너무 많은 오버 헤드없이 스레드 풀에서 개별적으로 실행될 수 있습니다 (또는 비동기 I/O 메소드 및 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를 대상으로합니다. 그러므로'Backgroundworkers' 대신'Threads'를 구현했는데 문제를 성공적으로 해결했습니다. – Linesofcode

관련 문제