【发布时间】:2019-01-08 02:31:43
【问题描述】:
我一直在将自己从 BackGroundWorker 过渡到 Async/Await 编程。在大多数情况下,它非常简单。但是,今天我正在使用 Entity Framework 6,但效果并不好。我这里有一个程序,我正在执行大量条目。我想要对进度条的反馈,因为这是一个漫长的过程,由一些冗长的过程组成。
这是后台工作者;不会线程锁定 UI,我正在使用;
private void ButtonMRCUpdateAll(object sender, EventArgs e) {
cmdMRCUpdateAllExcelSheets.Enabled = false;
cmdMRCUpdateSingleClient.Enabled = false;
tsStatusBar.Value = 0;
tsStatusBar.Visible = true;
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += delegate {
DateTime YearAndMonth = new DateTime(dtpMRC.Value.Year, dtpMRC.Value.Month, 1);
List<string> List = new List<string>();
using (wotcDB DB = new wotcDB()) {
var r = DB.client_main.
Where(t => t.Active == true).
OrderBy(t => t.CLIENTCODE).
Select(t => t.CLIENTCODE);
List.AddRange(r.ToArray());
}
bw.ReportProgress(0, List.Count);
int PercentComplete = 0;
foreach (var Client in List) {
using (wotcDB DB = new wotcDB()) {
DB.system_MRC(Client, YearAndMonth);
}
PercentComplete++;
bw.ReportProgress(PercentComplete);
}
};
bw.ProgressChanged += delegate (object s, ProgressChangedEventArgs ex) {
if (ex.UserState != null) { tsStatusBar.Maximum = (int)ex.UserState; }
tsStatusBar.Value = ex.ProgressPercentage;
};
bw.RunWorkerCompleted += delegate {
MessageBox.Show("Monthly Results and Changes - Task Complete");
cmdMRCUpdateAllExcelSheets.Enabled = true;
cmdMRCUpdateSingleClient.Enabled = true;
tsStatusBar.Visible = false;
tsStatusBar.Value = 0;
};
bw.RunWorkerAsync();
}
这是我尝试使用的 Async/Await。这个线程锁定了用户界面,我不知道为什么。我认为这是实体框架调用,因为我已经将 foreach 与其他非 EF 函数一起使用,并且运行良好。
private async void ButtonMRCUpdateAllExcelSheetsClick(object sender, EventArgs e) {
await ProcessEntries();
}
private async Task ProcessEntries() {
cmdMRCUpdateAllExcelSheets.Enabled = false;
cmdMRCUpdateSingleClient.Enabled = false;
tsStatusBar.Value = 0;
tsStatusBar.Visible = true;
DateTime YearAndMonth = new DateTime(dtpMRC.Value.Year, dtpMRC.Value.Month, 1);
List<string> List = new List<string>();
using (wotcDB DB = new wotcDB()) {
var r = DB.client_main.
Where(t => t.Active == true).
OrderBy(t => t.CLIENTCODE).
Select(t => t.CLIENTCODE);
List.AddRange(await r.ToArrayAsync());
}
tsStatusBar.Maximum = List.Count;
foreach (var Client in List) {
await AsyncProcessEntry(Client, YearAndMonth);
tsStatusBar.Value += 1;
}
cmdMRCUpdateAllExcelSheets.Enabled = true;
cmdMRCUpdateSingleClient.Enabled = true;
tsStatusBar.Visible = false;
tsStatusBar.Value = 0;
MessageBox.Show("Monthly Results and Changes - Task Complete");
}
private Task<string> AsyncProcessEntry(string Client, DateTime? YearAndMonth) {
var tcs = new TaskCompletionSource<string>();
using (wotcDB DB = new wotcDB()) {
DB.system_MRC(Client, YearAndMonth);
}
tcs.SetResult("");
return tcs.Task;
}
我在导致 UI 线程锁定的 Async/Await 中做错了什么?
编辑(system_MRC);
ALTER PROCEDURE [dbo].[system_MRC]
-- Add the parameters for the stored procedure here
@CLIENTCODE AS VARCHAR(16),
@Date AS DATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
~~ A whole lot of math stuff, then some inserts/updates based on if the entries exist already.
END
【问题讨论】:
-
您不需要任何代码。 BGW 尤其过时。使用
.ToListAsync()或.ToArrayAsync()等异步执行EF查询,即var list= await r.ToListAsync(); -
另一个问题是
async void仅适用于事件处理程序。它不能等待。如果要创建不返回任何结果的异步方法,请使用async Task -
这是一个程序,所以我不想得到回报。有没有办法异步运行程序?
-
是的,使用
async Task,而不是async void。 -
当您
await时,默认 行为是尊重同步上下文,然后通过它返回 - 这对您来说意味着“在用户界面线程";AsyncProcessEntry在执行异步操作之前是否碰巧做了很多非异步工作?因为如果是这样:那将发生在 UI 线程上。ConfigureAwait(false)可能会有所帮助,但是 a:UI 位 需要 在 UI 线程上,并且 b:如果AsyncProcessEntry进行重要的非异步工作,则需要将其移出 UI 线程
标签: c# multithreading tsql asynchronous entity-framework-6