问题起因:调用线程必须为STA,因为许多UI组件都需要
查找资料:
方案1:
Thread t = new Thread(new ThreadStart(delegate{
///do something
}));
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
方案2:window.Dispatcher.Invoke(new Action(delegate{
}));
因为wpf作为的是类库。所有的窗体都没有初始化,谈毛的方案2啊。
方案1 可以在委托中处理窗体。但是有个坑,外部每次调用都会重新new线程,在线程保证执行完成的情况下,依然会有内存泄露的情况,不论怎么搞都会泄露。
结论:window.Dispatcher.Invoke 不会泄露内存,但是没有一个主窗体供调用。ApartmentState.STA 能调用,但是内存泄露。
最终解决方案:将方案1和方案2 整合到一起,解决内存泄露问题。
思路:初始化一个STA线程作为全局变量。线程的委托方法 内部是个死循环+线程阻塞。通过控制参数达到控制 STA线程内部的运行。
代码:
public class UIThread
{
//是否同步
private bool Async { get; set; }
//窗体要加载的数据
private JObject Msg { get; set;}
//要调用的窗体名称
private string WinName { get; set; }
public void Dispatch(string winName,JObject msg)
{
Init();
this.Msg = msg;
this.WinName = winName;
this.Async = true;
//唤醒子线程
areA.Set();
//让主线程等待
areB.WaitOne();
}
//主线线程和子线程同步用到的两个东东,不晓得是个什么鬼
private static AutoResetEvent areA = new AutoResetEvent(false);
private static AutoResetEvent areB = new AutoResetEvent(false);
//委托方法,将当前对象传进来
private void Invoke(object obj)
{
UIThread p = (UIThread)obj;
//创建主窗体,主要是想调用它的Dispatcher.Invoke
Window win = new Window();
//死循环,让线程一直存活
while (true)
{
//等待主线程唤醒
areA.WaitOne();
win.Dispatcher.Invoke(new Action(delegate {
///调用UI,乱写的,懂那个意思就行
TestWin tw = new TestWin(p.Msg,p.winName);
tw.ShowDialog();
//子线程运行完成,唤醒主线程
areB.Set();
//继续执行到areA.WaitOne();停止,等待唤醒
}));
}
}
//STA线程是否初始化
private bool b;
//初始化STA线程
private void Init()
{
if (b) return;
b = true;
//不晓得是个什么卵
ParameterizedThreadStart pst = new ParameterizedThreadStart(Invoke);
Thread t = new Thread(pst);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
//当前对象作为参数,干进来
t.Start(this);
}
}
调用:先干个全局变量
public static UIThread uit = new UIThread();
走起:
uit.Dispatch(winName, result);
缺点:不能并发,不然扯到蛋!