2017-10-27 27 views
0

현재 실행중인 경우 백그라운드 작업자를 취소하고 다른 작업을 시작하려고합니다. 작업자가이 후 StartServerGetIP.RunWorkerAsync();차단 배경 작업자 및 Application.DoEvents에

에 취소되면

이 난에 못생긴 해결책을 발견 ... 함수에서 취소를 더 검사가 먼저

private void StartWorker() 
    { 
     if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); } 
     StartServerGetIP.RunWorkerAsync(); 
    } 
private void StartServerGetIP_DoWork(object sender, DoWorkEventArgs e) 
    { 
     StartFTPServer(Port, Ringbuf, sender as BackgroundWorker, e); 
     if ((sender as BackgroundWorker).CancellationPending) return; 
     GetIP(Ringbuf, sender as BackgroundWorker, e); 
    } 

private void StartServerGetIP_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Cancelled) 
     { 
      return; 
     } 
     if (e.Result.ToString() == "Faulted") 
     { 
      tcs.SetResult(false); 
      return; 
     } 

     Client.IPAddress = e.Result.ToString(); 
     tcs.SetResult(true); 
    } 

이 접근 블록이 시도

 private void StartWorker() 
    { 
     if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); } 
     while(StartServerGetIP.IsBusy) { Application.DoEvents(); } 

     StartServerGetIP.RunWorkerAsync(); 
    } 

인가가 나는를 호출하지 않고 그 날이 비동기 배경 노동자를 취소하고 다른 시작 할 수 있도록 구현할 수있는 패턴?

편집 : 취소 버튼은 문제가되지 않습니다.

편집 :

private void StartFTPServer(SerialPort port, RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args) 
    { 
     Stopwatch timeout = new Stopwatch(); 
     TimeSpan max = TimeSpan.FromSeconds(MaxTime_StartServer); 
     int time_before = 0; 
     timeout.Start(); 
     while (!buffer.Return.Contains("Run into Binary Data Comm mode...") && timeout.Elapsed.Seconds < max.Seconds) 
     { 
      if (timeout.Elapsed.Seconds > time_before) 
      { 
       time_before = timeout.Elapsed.Seconds; 
       sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds)); 
      } 
      if (sender.CancellationPending) 
      { 
       args.Cancel = true; 
       return; 
      } 
     } 
     port.Write("q"); //gets into menu 
     port.Write("F"); //starts FTP server 
    } 

     private void GetIP(RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args) 
    { 
     //if longer than 5 seconds, cancel this step 
     Stopwatch timeout = new Stopwatch(); 
     TimeSpan max = TimeSpan.FromSeconds(MaxTime_GetIP); 
     timeout.Start(); 
     int time_before = 0; 
     string message; 

     while (!(message = buffer.Return).Contains("Board IP:")) 
     { 
      if (timeout.Elapsed.Seconds > time_before) 
      { 
       time_before = timeout.Elapsed.Seconds; 
       sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds + MaxTime_StartServer)); 
      } 
      if (timeout.Elapsed.Seconds >= max.Seconds) 
      { 
       args.Result = "Faulted"; 
       return; 
      } 
      if (sender.CancellationPending) 
      { 
       args.Cancel = true; 
       return; 
      } 
     } 
     Regex regex = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"); 
     string IP = message.Remove(0, "Board IP: ".Length); 
     if (regex.IsMatch(IP)) 
     { 
      args.Result = IP; 
      ServerAlive = true; 
     } 
    } 

뿐만 아니라 당신의 StartServerGetIP_DoWork 방법에 .. 너무 당신에게

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace FPGAProgrammerLib 
{ 
    class RingBuffer<T> 
    { 
     T [] buffer { get; set; } 

     int _index; 
     int index 
     { 
      get 
      { 
       return _index; 
      } 
      set 
      { 
       _index = (value) % buffer.Length; 
      } 
     } 


     public T Add 
     { 
      set 
      { 
       buffer[index++] = value; 
      } 
     } 

     public T Return 
     { 
      get 
      { 
       return (index == 0) ? (IsString() ? (T)(object)string.Empty : default(T)) : buffer[--index]; 
      } 
     } 

     private bool IsString() 
     { 
      return (typeof(T) == typeof(string) || (typeof(T) == typeof(String))); 
     } 

     public RingBuffer(int size) 
     { 
      buffer = new T[size]; 
      index = 0; 
     } 
    } 
} 
+0

적) ('Application.DoEvents를 사용하지 마십시오 '. 그것은 위험으로 가득차 있습니다. 매우 조심 스럽거나 운이 좋으면 코드에서 오류를 디버그하는 것이 매우 어려워집니다. VB6 업그레이드를위한 이전 버전과의 호환성을 위해서만 프레임 워크에 있습니다. – Enigmativity

+0

이것을 수행하는 훨씬 좋은 방법이 있습니다. 전체 코드를 게시 할 수 있습니까? 귀하의 코드를 복사, 붙여 넣기 및 실행할 수 있기를 바랍니다. – Enigmativity

+0

당신이 지금하고있는 방식은 항상 * 근본적인 경주입니다. CancelAsync()가 실제로 tcs.Result가 설정되지 않는다고 보장 할 수는 없습니다. Undebuggable뿐만 아니라, 이것은 일주일에 한 번 잘못됩니다. StartWorker()를 실패하거나 절대로 호출 할 수 없도록 올바른 솔루션을 제안 할 수있는 코드가 충분하지 않습니다. –

답변

0

를 링 버퍼를 줄 수도 ... 내부 방법에 대해 묻는 사람들을위한 StartFTPServer 방법이있다. 취소가 요청 된 경우 해당 방법을 체크 인하 지 않았다고 가정합니다. 당신의 GetIP 방법에도 똑같이 적용됩니다. 그것들은 아마도 당신의 방해 요소 일 것입니다. 실제로 작업 취소를 확인하려면 취소 요청 여부를 주기적으로 확인해야합니다. 그래서 StartFTPServer 및 GetIP에 비동기 메서드를 사용하여 배경 작업자에게 취소 요청이 있는지 확인하는 것이 좋습니다.

StartFTPServer 메서드 또는 GetIP 메서드에서 수행 한 정확한 구현을 알지 못합니다. 코드를 리팩토링하는 방법에 대한 자세한 내용은 코드 게시를 취소 할 수 있습니다.

0

다음은 Microsoft의 Reactive Framework (Rx)를 사용하여 다른 스레드에서 작동중인 기내 기능을 효과적으로 취소하는 간단한 방법입니다.

Func<string> GetIP =() => ...; 

그리고 당신은 트리거의 일종이 - 등의 버튼 클릭이나 타이머를, 수 - 또는 내 경우에는 내가 ': 값을 반환하는 장기 실행 기능

시작은 당신이 원하는 m Rx 라이브러리의 유형을 사용합니다. 동안 새로운 통화를 시작하면

trigger.OnNext(Unit.Default); 

: 나는이 호출 할 수있는 기능을 시작하려는 언제 이제

IObservable<string> query = 
    trigger 
     .Select(_ => Observable.Start(() => GetIP())) 
     .Switch() 
     .ObserveOn(this); 

IDisposable subscription = 
    query 
     .Subscribe(ip => { /* Do something with `ip` */ }); 

:

Subject<Unit> trigger = new Subject<Unit>(); 

그런 다음이 코드를 작성할 수 있습니다 기존 호출이 실행 중일 때 기존 호출이 무시되고 마지막 호출 만 완료되면 쿼리에 의해 생성되고 구독이이를 가져옵니다.

쿼리가 계속 실행되고 모든 트리거 이벤트에 응답합니다.

.ObserveOn(this) (사용자가 WinForms를 사용한다고 가정) 결과가 다시 UI 스레드로 전달됩니다.

그냥 NuGet "System.Reactive.Windows.Forms"비트를 가져옵니다. 당신이 trigger이 버튼을 클릭하고 싶다면

, 이렇게 :

IObservable<Unit> trigger = 
    Observable 
     .FromEventPattern<EventHandler, EventArgs>(
      h => button.Click += h, 
      h => button.Click -= h) 
     .Select(_ => Unit.Default);