为啥异步等待操作仍然很耗时
Posted
技术标签:
【中文标题】为啥异步等待操作仍然很耗时【英文标题】:Why async await operation is still time consuming为什么异步等待操作仍然很耗时 【发布时间】:2021-11-21 02:24:10 【问题描述】:我有一个 Winforms 应用程序,我正在尝试打印一个上面有多个图层的 pdf 文档。
但问题是,所有操作都在 UI 线程上运行,并且长时间挂起 UI(无响应)。
我知道,这是因为 UI 线程被阻塞而发生的,所以我尝试借助强大的 async/await
关键字使此操作异步,但我的长期运行方法仍然不是异步的。它不是来自await
任务,并且操作与同步操作一样花费相同的时间。
我尝试了什么:
请看下面:
/// <summary>
/// Show Print Dialog
/// </summary>
private void ShowPrintDialog()
// Initialize print dialog
System.Windows.Controls.PrintDialog prtDialog = new System.Windows.Controls.PrintDialog();
prtDialog.PageRangeSelection = PageRangeSelection.AllPages;
prtDialog.UserPageRangeEnabled = false;
_printOptions.PrintQueue = null;
_printOptions.PrintTicket = null;
Enabled = false;
// if there is a default printer then set it
string defaulPrinter = prtDialog.PrintQueue == null ? string.Empty : prtDialog.PrintQueue.FullName;
// Display the dialog. This returns true if the user selects the Print button.
if (prtDialog.ShowDialog() == true)
_printOptions.PrintQueue = prtDialog.PrintQueue;
_printOptions.PrintTicket = prtDialog.PrintTicket;
_printOptions.UseDefaultPrinter = (defaulPrinter == prtDialog.PrintQueue.FullName);
// Re-enable the form
Enabled = true;
/// <summary>
/// Event raised when user clicks Print
/// </summary>
/// <param name="sender">Source of the event</param>
/// <param name="e">Event specific arguments</param>
private void cmdOk_Click(object sender, EventArgs e)
ShowPrintDialog();
if (_printOptions.PrintTicket != null)
//Set search Options
_print.ExportDataItem = true;
_print.FileName = SearchTemplateName;
//shows progress bar form.
using (frmPrintSearchResultsProgress frmProgress =
new frmPrintSearchResultsProgress(_print, this, _printOptions))
frmProgress.ShowDialog(this);
if (_print.ExportDataItem && !_print.DataItemExported && !_print.CancelExport)
MessageBox.Show("No Document printed.");
//Store selected options for current user
SaveOptions();
if (!SkipExport)
Close();
/// <summary>
/// Event raised when progress form is shown.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void frmExportSearchResultsProgress_Shown(object sender, EventArgs e)
try
Application.DoEvents();
dispatcher = Dispatcher.CurrentDispatcher;
// record export/print job start time
_startedUtc = DateTime.UtcNow;
_print.WritingToPdfIndicator = lblWritingPdfFile;
lblProgress.Text = Properties.Resources.PrintSearchResults;
await dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(DoDataItemPrint));
/// <summary>
/// Prints the selected data items.
/// </summary>
private void DoDataItemPrint()
// LONG RUNNING OPERATIONS..
// THIS OPERATION IS BLOCKING THE UI.
所以,正如上面代码中提到的,当我打开PringDialogForm
时,它正在打开一个进度条表单以查看打印文档的进度,并从这里触发frmExportSearchResultsProgress_Shown()
事件,在其中,我正在调用DoDataItemPrint()
方法很耗时。
所以,我尝试将frmExportSearchResultsProgress_Shown
事件设置为async/await
,但仍然需要与之前相同的时间。
谁能告诉我哪里做错了?
【问题讨论】:
所有代码都在 UI 线程上运行。没有什么异步的 请删除Application.DoEvents()
。这是邪恶的。它仅在与 2001 年的 VB6 兼容的框架中。它可能会导致重入问题,甚至锁定您的 UI。您应该立即删除它。
【参考方案1】:
您的 frmExportSearchResultsProgress_Shown
方法在 UI 线程上启动
然后它将DoDataItemPrint
分派到...同一个 UI 线程
它安排一个延续(通过await
),这样当不完整的事情发生时,我们回到frmExportSearchResultsProgress_Shown
,由于这里可能有一个同步上下文,同步上下文捕获(隐含在@ 987654325@) 会将我们推向... UI 线程
如您所见:一切都发生在UI 线程上。
如果你不想阻塞 UI,你需要离开 UI 线程。这可以像使用Task.Run
调用DoDataItemPrint
一样简单,但是如果不知道该代码包含什么,就不可能知道您是否使用线程绑定控件进行打印。如果你是......将很难摆脱它。
【讨论】:
以上是关于为啥异步等待操作仍然很耗时的主要内容,如果未能解决你的问题,请参考以下文章