【发布时间】:2019-11-27 09:52:37
【问题描述】:
我创建了用于将数据导出到 Excel 的应用程序。创建 Excel 文件的时间大约需要 10 分钟,所以我决定在另一个线程中执行此操作,而不阻塞 UI 线程,以便用户可以继续使用 app.我有一个用于导出数据的 .dll,而且我在 Winforms 中多次做过同样的事情,完全没有问题。我的 .dll 文件将取消标记作为参数,因此用户可以随时取消导出,这就是我更喜欢使用此 .dll 的原因。
不幸的是,我对 WPF 很陌生,所以我不知道如何创建与 Winforms 中相同的东西。这是我的代码 sn-p:
public CancellationTokenSource cancel_task = new CancellationTokenSource();
private async void Button1_Click(object sender, RoutedEventArgs e)
{
//Doing some work in UI thread before calling Task.Run …
await Task.Run(() =>
{
Export_to_Excel(dict_queries, dtp.Value);
}, cancel_task.Token);
}
void Export_to_Excel(Dictionary<string, int> queries, DateTime? date)
{
try
{
using (var con = new OracleConnection(conn_string))
{
con.Open();
//Fetching DB Command to start with exporting data ...
//Inicializing my .dll method, added as reference to project
var export_xlsx = new Excel_export();
//Here is where It starts to block UI thread
Application.Current.Dispatcher.Invoke(() => { export_xlsx.Export_command(cmd_export,cancel_task.Token);});
}
}
catch (Exception ex)
{
MessageBox.Show (ex.Message);
}
}
如果我只使用这一行export_xlsx.Export_command(cmd_export,cancel_task.Token);
而不是Application.Current.Dispatcher.Invoke...,然后我得到“System.Threading.ThreadStateException:当前线程必须设置为单线程单元(STA)模式,然后才能进行OLE调用......”错误。这在我看来就像一个 .dll 回到了 UI 线程上,即使它是从非 UI 线程调用的......但是这条线在 Winforms 中非常适合我,所以我不知道这里有什么问题。
我尝试了 Task.Run 或 Task.Factory.StartNew 的许多不同变体(就像我通常在 Winforms 中所做的那样),但一切都导致我阻塞 UI 线程或不同类型的错误。
我的设计是带框架的主窗口,在该框架内我打开一个页面,单击按钮导出数据。如前所述,我是 WPF 的新手,所以也许这导致了我的问题。任何建议都非常感谢!
编辑:
我想我知道出了什么问题。在我的 .dll 代码中,我调用了 SaveFileDialog.ShowDialog(),据我所知,它是一个 UI 线程操作。不幸的是,我在这个应用程序中需要它,在这种情况下我能做些什么吗?
我猜这就是为什么在 Winforms 中一切正常的原因,因为我不在那里使用 SaveFileDialog。
【问题讨论】:
-
您是否尝试过启动线程(不是任务)并使用方法
Thread.SetApartmentState将Thread.ApartmentState属性设置为值ApartmentState.STA? -
@TheodorZoulias,我试过了,但我不知道在哪里把它放在代码中。可能有点指导?
-
在
Button1_Click处理程序中,不要使用await Task.Run启动任务,而是创建并启动一个运行Export_to_Excel方法的新线程。并在启动前设置线程的ApartmentState。 -
@TheodorZoulias,我收到与 Erwin 发布的解决方案相同的错误,只是这次它在我调用方法 Export_to_Excel 时显示错误。
-
你能在
Task.Run()之后贴上ConfigureAwait(false),看看有什么变化吗?
标签: c# wpf multithreading task multitasking