Dan*_*man 3 c# multithreading task office-interop
当我重构一些旧的 C# 代码以使用该库生成文档时Office.Interop,我发现了这一点,因为它使用了 UI 上下文。当从它调用函数时它会阻塞它
例如:
private void btnFooClick(object sender, EventArgs e)
{
bool documentGenerated = chckBox.Checked ? updateDoc() : newDoc();
if(documentGenerated){
//do something
}
}
Run Code Online (Sandbox Code Playgroud)
我决定更改它以减少阻塞 UI:
private async void btnFooClick(object sender, EventArgs e)
{
bool documentGenerated; = chckBox.Checked ? updateDoc() : newDoc();
if(chckBox.Checked)
{
documentGenerated = await Task.Run(() => updateDoc()).ConfigureAwait(false);
}
else
{
documentGenerated = await Task.Run(() => newDoc()).ConfigureAwait(false);
}
if(documentGenerated){
//do something
}
}
Run Code Online (Sandbox Code Playgroud)
它抛出了这个错误:
Current thread must be set to single thread apartment (STA) mode
before OLE calls can be made
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种情况以及解决方法是什么?
通过 Interop 访问的 COM 组件要求调用线程是 STA 线程,但在您的情况下它不是 STA。否则,可以通过多个线程访问 STA 组件。您可以在了解和使用 COM 线程模型中阅读有关为什么需要 STA 的更多信息。
您可以按照在任务上设置 ApartmentState中的建议在任务类上创建扩展方法,以使用任务通过 Interop 调用 COM 组件:
public static Task<T> StartSTATask<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
Thread thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
当您使用Thread而不是task时,您必须使用类似的方法将ApartmentState设置为STA thread.SetApartmentState(ApartmentState.STA)。