Porównaj ceny domen i usług IT, sprzedawców z całego świata

Dlaczego BackgroundWorker jest zawsze zajęty?


Odkryłem coś dziwnego o moim programie roboczym w tle w mojej aplikacji WPF.
To, co próbuję teraz zrobić, to zaczekać, aż BW zakończy się, aby rozpocząć kolejny wątek.
Sprawdź poniższy kod:
if (bw.IsBusy)
{
bw.CancelAsync(); System.Threading.ThreadStart WaitThread =
new System.Threading.ThreadStart(delegate() {
while (bw.IsBusy)
{
System.Threading.Thread.Sleep(100);
} bw.RunWorkerAsync();
}); System.Windows.Application.Current.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal,
WaitThread);// if I remove this line, bw fires RunWorkerAsyncEvent
}
else
{
bw.RunWorkerAsync();
}

Zwróć uwagę, że dodałem Dispatcher.Invoke, aby czekać, aż bw będzie zajęty, ale będzie zajęty przez cały czas, jeśli zadzwonię i nigdy nie uruchomię zdarzenia
RunWorkerAsyncCompleted
. Chociaż, jeśli usunę tę linię, FIRES jest zdarzeniem i jest naprawdę dziwne.
Jak mogę czekać, aż skończy się BW?
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nazywa się to „zakleszczeniem” i jest bardzo częstym problemem związanym z wątkami. BGW nie może przestać być zajęty, dopóki nie nastąpi zdarzenie RunWorkerCompleted. To zdarzenie jest wykonywane w głównym wątku twojej aplikacji, może być wykonane tylko wtedy, gdy twój główny wątek nie jest zajęty czymś innym. Musi być bezczynny, działający w pętli dyspozytora.
Ale twój główny wątek nie jest bezczynny, utknął w pętli while (), czekając, aż IsBusy zwróci wartość false. W ten sposób zdarzenie nigdy nie może zostać uruchomione, ponieważ główny wątek jest zajęty czekaniem na zakończenie BGW, a BGW nie może zakończyć, ponieważ główny wątek nigdy nie jest bezczynny. „Deadly Embrace”, czyli ślepy zaułek.
Będziesz musiał to zrobić inaczej, nie możesz czekać. Powiedzmy, tworząc kolejną instancję BGW. Lub przeniesienie kodu po oczekiwaniu na procedurę obsługi zdarzenia RunWorkerCompleted. Jeśli jest aktywowany, powiedzmy, zdarzeniem kliknięcia przycisku, pamiętaj, aby wyłączyć przycisk podczas wywoływania RunWorkerAsync (), włącz go ponownie w RunWorkerCompleted.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Zakleszczenie występuje, ponieważ „główny” wątek aplikacji nie może działać (i obsługiwać zdarzeń), więc Backgroundworker nigdy nie może uruchomić swojej zakończonej funkcji. Co możesz zrobić, aby aplikacja działała, to dodaj Application.DoEvents ();
To jest to, czego obecnie używam w podobnym scenariuszu i wygląda na to, że działa jak zaklęcie!
while (bw.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie jestem pewien, czy dobrze zrozumiałem, ale myślę, że próbujesz ponownie uruchomić

BackgroundWorker

(więc jeśli jest zajęty, najpierw zatrzymaj go, a następnie uruchom ponownie), jeśli tego chcesz, spróbuj tego:
zadeklaruj to

delegat

gdzieś w Twojej klasie:
private delegate bool StateChecker();

i użyj tego kodu, aby ponownie uruchomić

BackgroundWorker

:
StateChecker stillWorking = () => { return bw.IsBusy; }; if (bw.IsBusy)
{
bw.CancelAsync();
while ((bool)this.Dispatcher.Invoke(stillWorking, null)) Thread.Sleep(15);
}
bw.RunWorkerAsync();
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto kompletny i działający kod.

C # (klasa okna

)
public partial class ForceSyncWindow : Window
{
BackgroundWorker backgroundWorker = new BackgroundWorker(); public ForceSyncWindow()
{
InitializeComponent(); ProgressBar1.Visibility = System.Windows.Visibility.Hidden; backgroundWorker.WorkerSupportsCancellation = true;// To report progress from the background worker we need to set this property
backgroundWorker.WorkerReportsProgress = true;// This event will be raised on the worker thread when the worker starts
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);// This event will be raised when we call ReportProgress
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
} void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
ProgressBar1.Value = 0;
// TODO LOG = "Canceled"; }
else
{
// Finally, handle the case where the operation
// succeeded.
// TODO LOG e.Result.ToString(); } ProgressBar1.Value = 0;
ProgressBar1.Visibility = System.Windows.Visibility.Hidden;// Enable the Synchronize button.
this.Synchronize.IsEnabled = true;// Disable the Cancel button.
this.Cancel.IsEnabled = false;
}// On worker thread so do our thing!
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
if (backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
// Report progress to 'UI' thread
backgroundWorker.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100); ;
}
}
}// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
ProgressBar1.Value = e.ProgressPercentage;
} private void Synchronize_Click(object sender, RoutedEventArgs e)
{
ProgressBar1.Value = 0;
ProgressBar1.Visibility = System.Windows.Visibility.Visible;// Disable the Synchronize button.
this.Synchronize.IsEnabled = false;// Enable the Cancel button.
this.Cancel.IsEnabled = true;// Start the background worker
backgroundWorker.RunWorkerAsync();
} private void Cancel_Click(object sender, RoutedEventArgs e)
{
if (backgroundWorker.IsBusy)
{
// Cancel the background worker
backgroundWorker.CancelAsync();
}
}
}


XAML

<Window x:Class="MySyncManager.Views.ForceSyncWindow"
xmlns="[url=http://schemas.microsoft.com/winfx/2006/xaml/presentation"]http://schemas.microsoft.com/w ... ot%3B[/url]
xmlns:x="[url=http://schemas.microsoft.com/winfx/2006/xaml"]http://schemas.microsoft.com/winfx/2006/xaml"[/url] Title="ForceSyncWindow" Height="300" Width="509" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid> <Button Content="Synchronize" Name="Synchronize" HorizontalAlignment="Left" Margin="411,10,0,0" VerticalAlignment="Top" Width="75" Click="Synchronize_Click"/>
<RichTextBox HorizontalAlignment="Left" Height="132" Margin="10,116,0,0" VerticalAlignment="Top" Width="476"> </RichTextBox>
<Button Content="Cancel" x:Name="Cancel" HorizontalAlignment="Left" Margin="411,40,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.508,2.154" Click="Cancel_Click"/>
<ProgressBar Name="ProgressBar1" HorizontalAlignment="Left" Height="10" Margin="10,101,0,0" VerticalAlignment="Top" Width="476"/> </Grid>
</Window>

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się