我制作了带有进度条的WPF应用程序。在功能“ Background_Work”的“ while”循环中,进度条的值将更新为随机值。此函数在单独的线程中运行。 如果我关闭窗口,则想结束该函数(通过结束“ while”循环)并加入线程。
问题是,在某些情况下,窗口会冻结并且不会关闭。
public partial class MainWindow : Window
{
Random random;
Thread background_work;
bool running;
public MainWindow()
{
InitializeComponent();
random = new Random();
background_work = new Thread(Background_Work);
running = true;
background_work.IsBackground = true;
background_work.Start();
}
private void Background_Work()
{
while (running)
{
myBar.Dispatcher.Invoke(() => { myBar.Value = random.Next(0, 100); });
Thread.Sleep(1000);
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
running = false;
background_work.Join();
}
}
答案 0 :(得分:1)
我认为发生的事情是两件事的结合,两者共同导致了僵局。
第一个是running
字段是从多个线程访问的,但是没有同步。如果您从多个线程访问数据,则必须 被锁定。在这种情况下,主线程将该字段设置为false后,后台线程可能会在一段时间内将其读取为true。
第二个是您致电Dispatcher.Invoke
,而不是Dispatcher.BeginInvoke
。 Invoke
会将消息发布到调度程序的消息队列中,并等待,直到该消息被处理,从而阻止了调用线程。
那么会发生什么:
running
设置为false
running
读为true
,将消息发布到调度程序线程的消息队列中,并阻止等待它被处理之所以会这样,是因为您违反了许多不同的基本规则:
正如评论所说,为此使用DispatcherTimer。
如果确实需要取消后台线程,请使用CancellationToken
。
答案 1 :(得分:0)
在等待background_work.Join()
线程自然结束时,调用background_work
会阻塞当前线程。您在while
循环中忙碌着,所以它永远不会结束,因此您的UI会永远等待着。
我建议您避免所有线程并在一起使用Microsoft的Reactive Framework(NuGet System.Reactive.Windows.Threading
作为WPF位)。
然后您可以执行以下操作:
public partial class MainWindow : Window
{
Random random;
IDisposable subscription;
public MainWindow()
{
InitializeComponent();
random = new Random();
subscription =
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.ObserveOnDispatcher()
.Subscribe(_ => myBar.Value = random.Next(0, 100));
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
subscription.Dispose();
}
}
这使事情变得非常简单。