我在WPF应用程序中的后台工作程序中发现了一些奇怪的东西。
我现在要做的就是等到BW完成另一个线程。
检查以下代码:
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();
}
请注意我添加了Dispatcher.Invoke以等待bw不忙,但如果我调用它并且从不触发RunWorkerAsyncCompleted
事件,则所有时间都很忙。虽然,如果我删除该行,FIRES事件,这真的很奇怪。
我怎么能等到bw结束?
答案 0 :(得分:25)
这称为“死锁”,这是一个非常常见的线程问题。在运行RunWorkerCompleted事件之前,BGW无法停止忙碌。该事件在您的应用程序的主线程上运行,它只能在您的主线程不忙于执行其他操作时运行。它必须是空闲的,在调度程序循环内运行。
但是你的主线程没有空闲,它被卡在while()循环中,等待IsBusy返回false。所以事件永远不会运行,因为主线程忙于等待BGW完成,BGW无法完成,因为主线程永远不会空闲。 “致命的拥抱”,又名死锁。
你必须以不同的方式做到这一点,你不能等待。比如创建另一个BGW实例。或者只是在RWC事件触发之前不允许此代码运行。或者通过设置一个标志,由RunWorkerCompleted事件处理程序测试,然后可以再次启动BGW。
答案 1 :(得分:4)
由于应用程序的“主”线程无法运行(并处理事件)而发生死锁,因此Backgroundworker永远无法触发它的完成函数。您可以做什么来允许应用程序触发它是添加Application.DoEvents();
这是我目前在类似场景中使用的内容,它看起来像一个魅力!
while (bw.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
答案 2 :(得分:3)
我不确定我是否理解正确,但我认为你正在尝试重新启动 BackgroundWorker (所以如果它很忙,你先停止它然后再启动它),如果这是你想要的,试试这个:
在你班级的某个地方声明这个委托:
private delegate bool StateChecker();
并使用此代码重新启动 BackgroundWorker :
StateChecker stillWorking = () => { return bw.IsBusy; };
if (bw.IsBusy)
{
bw.CancelAsync();
while ((bool)this.Dispatcher.Invoke(stillWorking, null)) Thread.Sleep(15);
}
bw.RunWorkerAsync();
答案 3 :(得分:2)
这是完整且有效的代码。
C#(窗口级别)
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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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>
答案 4 :(得分:-1)
将其放在另一个线程中:
private void myButton_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker mainWorker = new BackgroundWorker();
mainWorker.DoWork += new DoWorkEventHandler(mainWorker_DoWork);
mainWorker.RunWorkerAsync();
}
void mainWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(int x = 0; x >= 100; x++)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync(argument: x);
while(worker.IsBusy)
{
Thread.Sleep(100);
}
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
string iam = "Hello i'm thread number: " + e.Argument.ToString();
//do something ...
}