BackgroundWorker를 올바르게 중지하는 방법
2 개의 콤보 박스가있는 양식이 있습니다. 그리고 채우려 combobox2.DataSource
를 기반으로 combobox1.Text
하고 combobox2.Text
(필자는 사용자의 입력을 완료했다고 가정 combobox1
하고있는 입력의 중앙에있다 combobox2
). 그래서 combobox2
다음과 같은 이벤트 핸들러가 있습니다 .
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
DataSource를 빌드하는 데 시간이 많이 걸리는 프로세스 (데이터베이스에 대한 요청을 생성하고 실행)에 관한 한, BackgroundWorker를 사용하여 다른 프로세스에서 수행하는 것이 더 낫다고 결정했습니다. 따라서 cmbDataSourceExtractor가 작업을 완료하지 않았고 사용자가 기호를 하나 더 입력하는 시나리오가 있습니다. 이 경우
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
BackgroundWorker가 사용 중이며 동시에 여러 작업을 수행 할 수 없다는 예외가이 줄 에 표시됩니다.
이 예외를 제거하는 방법은 무엇입니까?
미리 감사드립니다!
CancelAsync
실제로 스레드 또는 이와 유사한 것을 중단하지 않습니다. 를 통해 작업을 취소해야한다는 메시지를 작업자 스레드에 보냅니다 BackgroundWorker.CancellationPending
. 백그라운드에서 실행중인 DoWork 대리자는 주기적으로이 속성을 확인하고 취소 자체를 처리해야합니다.
까다로운 부분은 DoWork 대리자가 차단 중일 가능성이 있다는 것입니다. 즉, 다른 작업을 수행하기 전에 DataSource에서 수행하는 작업이 완료되어야합니다 (예 : CancellationPending 확인). 실제 작업을 또 다른 비동기 델리게이트로 옮기고 (또는 더 나은 방법은 작업을으로 제출 ThreadPool
)이 내부 작업자 스레드가 대기 상태를 트리거하거나 CancellationPending을 감지 할 때까지 주 작업자 스레드가 폴링하도록해야 할 수 있습니다.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx
CancelAsync ()와 RunWorkerAsync () 사이에 루프를 추가하면 문제가 해결됩니다.
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
Application.DoEvents () 호출이 포함 된 while 루프는 현재 작업자 스레드가 제대로 취소 될 때까지 새 작업자 스레드의 실행을 중단합니다. 작업자 스레드의 취소를 처리해야한다는 점을 염두에 두십시오. 다음과 같이 :
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
첫 번째 코드 조각의 Application.DoEvents ()는 GUI 스레드 메시지 대기열을 계속 처리하므로 cmbDataSourceExtractor.IsBusy 속성을 취소하고 업데이트하는 작업도 계속 처리됩니다 (Application.DoEvents () 대신 계속 추가 한 경우). 루프는 GUI 스레드를 사용 중 상태로 잠그고 이벤트를 처리하여 cmbDataSourceExtractor.IsBusy를 업데이트하지 않습니다.)
주 스레드와 BackgroundWorker간에 공유되는 플래그 (예 : BackgroundWorker.CancellationPending
. BackgroundWorker를 종료하려면 BackgroundWorker.CancelAsync ()를 사용하여 플래그를 설정하면됩니다.
MSDN에는 샘플이 있습니다 : http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx
내 예. DoWork는 다음과 같습니다.
DoLengthyWork();
//this is never executed
if(bgWorker.CancellationPending)
{
MessageBox.Show("Up to here? ...");
e.Cancel = true;
}
DoLenghtyWork 내부 :
public void DoLenghtyWork()
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
OtherStuff () 내부 :
public void OtherStuff()
{
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
원하는 것은 DoLenghtyWork와 OtherStuff ()를 모두 수정하여 다음과 같이 만드는 것입니다.
public void DoLenghtyWork()
{
if(!bgWorker.CancellationPending)
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
public void OtherStuff()
{
if(!bgWorker.CancellationPending)
{
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
이 문제는 cmbDataSourceExtractor.CancelAsync()
비동기 메서드이기 때문에 종료시 Cancel
작업이 아직 완료되지 않았기 cmdDataSourceExtractor.RunWorkerAsync(...)
때문에 발생합니다. 다시 cmdDataSourceExtractor
전화하기 전에 완료 될 때까지 기다려야 RunWorkerAsync
합니다. 이를 수행하는 방법 은이 SO 질문에 설명 되어 있습니다.
이 방법을 시도했지만 작동하지 않았기 때문에 내 대답이 약간 다릅니다. 내 코드는 데이터베이스 값을 읽거나 List 개체 또는 이와 같은 개체에 개체를 추가하기 직전에 선호하는 경우 공용 정적 클래스에서 부울 플래그를 확인하는 추가 클래스를 사용합니다. 아래 코드의 변경 사항을 참조하십시오. ThreadWatcher.StopThread 속성을 추가했습니다. 이 설명을 위해 저는 현재 스레드를 복원하려고합니다. 문제가 아니기 때문에 다음 스레드에 액세스하기 전에 속성을 false로 설정하는 것만 큼 쉽습니다.
private void combobox2_TextChanged(object sender, EventArgs e)
{
//Stop the thread here with this
ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}
괜찮아
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
이제 다음 클래스를 추가하십시오.
public static class ThreadWatcher
{
public static bool StopThread { get; set; }
}
그리고 당신이 데이터베이스를 읽는 수업에서
List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
break;
list.Add(something);
...
finally 블록을 사용하여 데이터베이스 연결 등을 올바르게 닫는 것을 잊지 마십시오. 도움이되기를 바랍니다. 도움이된다면 저를 표시해주세요.
In my case, I had to pool database for payment confirmation to come in and then update WPF
UI.
Mechanism that spins up all the processes:
public void Execute(object parameter)
{
try
{
var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, "transactionRef");
Process.Start(new ProcessStartInfo(url));
ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
}
catch (Exception e)
{
ViewModel.Log.Error("Failed to navigate to payments", e);
MessageBox.Show("Failed to navigate to payments");
}
}
Mechanism that does checking for completion:
private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(30000);
while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
{
Thread.Sleep(5000);
}
//Plug in pooling mechanism
this.AuthCode = GetAuthToken();
}
Mechanism that cancels if window gets closed:
private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
{
var context = DataContext as PaymentViewModel;
if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
context.UpdateUiWhenDoneWithPayment.CancelAsync();
}
I agree with guys. But sometimes you have to add more things.
IE
1) Add this worker.WorkerSupportsCancellation = true;
2) Add to you class some method to do the following things
public void KillMe()
{
worker.CancelAsync();
worker.Dispose();
worker = null;
GC.Collect();
}
So before close your application your have to call this method.
3) Probably you can Dispose, null
all variables and timers which are inside of the BackgroundWorker
.
ReferenceURL : https://stackoverflow.com/questions/4732737/how-to-stop-backgroundworker-correctly
'developer tip' 카테고리의 다른 글
Grails 애플리케이션의 세션 시간 제한을 구성하는 방법은 무엇입니까? (0) | 2020.12.15 |
---|---|
문자열의 첫 번째 문자를 축소하는 방법은 무엇입니까? (0) | 2020.12.15 |
후행 0 유지 (0) | 2020.12.15 |
C #에서 디렉터리 찾아보기 (0) | 2020.12.14 |
C #을 사용하여 시스템 가동 시간 검색 (0) | 2020.12.14 |