我正在编写一些在UI线程上调用的代码,在另一个线程(不是ThreadPool,但每次都是相同的线程)上调用一些代码,然后在UI线程上恢复。我想就最好的异步方式做一些建议。
EnsureThread方法的复杂性是因为另一个线程每次都必须是同一个线程,并且必须是运行Dispatcher的STA。这是因为我需要使用MCI,但不希望它在UI线程上运行。见https://stackoverflow.com/a/32711239/420159。
我像这样创建第二个线程:
private static void EnsureThread()
{
if (eventLoopThread != null)
{
return;
}
lock (eventLoopLock)
{
if (eventLoopThread == null)
{
var lck = new EventWaitHandle(false, EventResetMode.ManualReset);
var t = new Thread(() =>
{
try
{
// create dispatcher and sync context
var d = Dispatcher.CurrentDispatcher;
var context = new DispatcherSynchronizationContext(d);
SynchronizationContext.SetSynchronizationContext(context);
// create taskfactory
eventLoopFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
eventLoopDispatcher = d;
}
finally
{
lck.Set();
}
// run the event loop
Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
lck.WaitOne();
lck.Dispose();
eventLoopThread = t;
}
}
}
然后我像这样调用第二个线程:
async void button_click(...)
{
// do something 1
await eventLoopFactory.StartNew(()=>
{
// do something 2
});
// do something 3
}
有更好的方法吗?
答案 0 :(得分:4)
如果要在ThreadPool
只需使用
await Task.Run(() =>
{
// do something 2
});
这样,您就不需要在事件循环线程上运行// do something
代码,但它将在线程池中的可用线程上运行。
你不应该自己创建第二个帖子。线程池是正确的工具,因为它将以非常有效的方式回收空闲线程。
答案 1 :(得分:1)
您的Button_Click
似乎已经做了太多事情,您应该首先将此方法拆分为单独的方法。
async void Button_Click(...)
{
await DoSomething1();
await DoSomething2();
await DoSomething3();
}
现在,您的其他方法将如下所示:
async Task DoSomething1()
{
await Task.Run(() =>
{
...
});
}
这将允许您的Button_Click
方法异步执行这些任务(按顺序)并保持您的UI响应。