这是我的代码:
private void OpenLoadingWindow()
{
loadingWindow = new LoadingView();
loadingWindow.Closed += new EventHandler(LoadingWindow_Closed);
_go = true;
loadingWindow.ShowDialog();
}
public void OpenLoadingWindowInNewThread()
{
thread = new Thread(x => OpenLoadingWindow());
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
lock (_locker)
{
Monitor.Pulse(_locker);
}
}
public void CloseLoadingWindow()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker);
if (loadingWindow != null)
{
loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
{
_go = false;
loadingWindow.Close();
loadingWindow = null;
}));
}
}
在代码中我首先调用OpenLoadingWindowInNewThread()然后调用CloseLoadingWindow()。但是,第一次执行代码时它工作正常。但在那之后,BeginInvoke中的CloseLoadingWindow()中的代码不会被执行。我做错了什么?
我想要实现的是:打开加载窗口,执行一些代码。代码执行后我调用了闭包方法,我想关闭加载窗口。
答案 0 :(得分:2)
这里的主要问题是您正在为UI创建第二个线程。别这么做。
不幸的是,你没有提供一个好的代码示例。所以为了回答,让我们假设您正在做这样的事情:
void button1_Click(object sender, EventArgs e)
{
DoLoadingWork();
}
void DoLoadingWork()
{
OpenLoadingWindowInNewThread();
LoadingWork();
CloseLoadingWindow();
}
即。某些事件发生在您的用户界面中,现在您必须做一些工作。您通过调用问题中显示的方法,处理UI线程中的工作并创建第二个线程来显示对话框来实现此目的。
这是解决这个问题的错误方法。相反,您应该将所有UI保留在同一个线程中,并在另一个线程中完成工作。这看起来更像是这样:
void DoLoadingWork()
{
using (LoadingView form = new LoadingView())
{
form.Shown += async (sender, e) =>
{
await Task.Run(() => LoadingWork());
form.Close();
};
form.ShowDialog();
}
}
此版本执行以下操作:
Shown
事件,以确保在发生任何其他事件之前对话框可见LoadingWork()
方法LoadingWork()
方法完成时,对话框将关闭,允许处理对话框并返回DoLoadingWork()
方法。请注意,即使您必须从执行处理的代码与UI进行交互,或者您需要一种方法来中断处理,上述操作仍然是正确的方法。可以使用标准惯用法来轻松实现这些要求的其他方面。
如果没有关于该处理可能是什么的实际示例,以及UI交互和/或中断如何工作,则无法确切地说明该部分将如何实现。但它通常涉及使用Invoke()
进行UI交互(甚至更好,重构处理以使其使用async
/ await
,在await
语句之间进行UI交互工作的各个部分)和一个标志或CancellationToken
来处理中断线程。
如果您的处理实际上与UI进行了交互,并且实际上您确实在UI线程中运行了它,那么您可能已经调用了Refresh()
或{等方法{1}}穿插。实际上从不需要这些方法,并且恕我直言总是表示代码已被错误地实现。更改实施以将正确的代码放入正确的线程中的额外好处是,您不必使用任何这些方法与UI进行交互(相反,您将使用Application.DoEvents()
)。