假设我有一个通过按钮点击方法(winforms)触发的数据处理方法
业务对象
public class MembershipCard
{
public int ID { get; set; }
public string MemberCode { get; set; }
public string MemberName { get; set; }
public string MemberCard { get; set; }
public byte[] MemberCardImage { get; set; }
}
所以我想让应用程序更具响应性,比如在检索数据时我将多行文本框作为进程状态,在执行执行读取器时,它就像
Textbox1.Text = "Retrieving member data, Member Identity" + obj.MemberName ... ... + Environment.NewLine;
或处理时像“处理会员”......
类似的东西,因为数据有点大(> 100.000行),所以单击按钮后,应用程序将不会响应(显然没有响应)
我写的代码没有按照我想要的方式工作
using (var reader = mySql.ExecuteReader())
{
if (reader.HasRows)
{
MembershipCard obj;
while (reader.Read())
{
obj = new MembershipCard();
if (!reader.IsDBNull(reader.GetOrdinal("ID")))
{
obj.ID = Convert.ToInt32(reader["ID"]);
}
if (!reader.IsDBNull(reader.GetOrdinal("MemberCode")))
{
obj.MemberCode = Convert.ToString(reader["MemberCode"]);
}
if (!reader.IsDBNull(reader.GetOrdinal("MemberName")))
{
obj.MemberName = Convert.ToString(reader["MemberName"]);
}
if (!reader.IsDBNull(reader.GetOrdinal("MemberCard")))
{
obj.MemberCard = Convert.ToString(reader["MemberCard"]);
if (!string.IsNullOrEmpty(obj.MemberCard))
{
QRCode qrCode = new QRCode();
qrCode.Data = obj.MemberCard;
qrCode.ModuleSize = 3;
qrCode.LeftMargin = 0;
qrCode.RightMargin = 0;
qrCode.TopMargin = 0;
qrCode.BottomMargin = 0;
qrCode.UOM = UnitOfMeasure.PIXEL;
qrCode.Encoding = BarcodeLib.Barcode.QRCodeEncoding.Auto;
qrCode.ECL = BarcodeLib.Barcode.QRCodeErrorCorrectionLevel.L;
qrCode.ImageFormat = System.Drawing.Imaging.ImageFormat.Png;
obj.MemberCardImage = qrCode.drawBarcodeAsBytes();
}
}
textBox1.Text = "Getting member data, Member: " + obj.MemberName + "/" + obj.MemberCode + "/" + obj.MemberCard;
result.Add(obj);
}
reader.Close();
}
TLDR:执行SqlCommand.ExecuteReader()
时,实时在文本框上编写流程
提前致谢
答案 0 :(得分:1)
如今在winforms中有两种广泛用于在执行大型计算时保持程序响应的方法。一个是使用后台工作程序,另一个是使用async-await。
后台工作程序更适合于不经常启动的计算(不是每秒几次),并且需要相当长的时间(几秒钟,几分钟),因为启动后台工作程序意味着启动一个单独的线程当后台工作者需要访问主线程也访问的数据时所涉及的所有问题(死锁,InvokeRequired,...)。
如果您有相当短的计算可能必须经常启动,则使用Async-await。如果你不想要开始一个新线程的开销,并希望看起来像是相当同步的代码,你可以使用它。
我曾经为背景计算,检索数据,编写文件等创建后台工作者,但是现在我越来越多地使用async-await来做这件事。代码看起来更简单,更易于维护。
This interview with Eric Lippert帮助我了解async-await的工作原理。在中间某处搜索异步等待
在这篇文章中,Eric Lippert将async-await与准备一顿饭的厨师进行比较。每当他不得不等待某件事情完成时,他就会开始环顾四周,看看他是否可以做其他事情,而不是懒得等待。
同样是async-await。每当你的程序必须等待加载文件之类的东西,要执行的数据库查询,要下载的互联网数据,async-await程序四处寻找它是否可以做其他事情,比如响应用户输入
In this article the ever so helpful Stephen Cleary explains the basics of async-await.
主要特点:
Task
而不是void
和Task<TResult>
而不是TResult
void
。void
时,await的返回值为Task
,等待TResult
时等待Task<TResult>
。如果异步过程调用其他异步过程,则执行此其他过程中的代码,就好像它不是异步过程,直到满足等待。线程上升到其调用堆栈并执行代码,直到它遇到await,而不是什么也不做。线程再次上升到调用堆栈等等。一旦每个人都在等待,它将返回第一个等待直到此任务完成。因为用户界面无法等待你的程序完成,只要你的线程没有进行任何计算(只要你的厨师正在等待面包烤制),你就会确定,你的线程是免费的保持UI响应(厨师可以自由地做其他事情)
请注意,如果您的线程会进行计算,则不会等待某些内容,因此您的UI无法响应。因此,您应该启动另一个线程来执行计算,并且只在您需要时立即等待结果。
在您的情况下,以下代码可以保持您的UI响应。
public async void OnButton1_clicked(object sender, ...)
{
await this.UpdateData();
// I chose to split the event from the action, so others like menu items
// could also call UpdateData();
// Besides it is a nice example of a function that returns Task instead of void
}
请注意,虽然此事件处理程序声明为async,但它返回void。
public async Task UpdateData()
{
// start a Task that will read the database, but don't await
var myReadTask = Task.Run( () => ReadDatabaseData(...));
// because you are not awaiting you are free to do other things here
// like informing the operator that the data is being read:
// note that this code is performed by the thread that has the UI context
// so you are free to use the textbox
Textbox1.Text = "Retrieving member data ...";
// once you need the data await for it. The return value is the "TResult"
MyReadData data = await myReadTask;
// here you are certain that the data has been read
Textbox1.Text = "Finished reading data";
ProcessReadData(data);
}
UpdateData
返回Task
而不是void
。它被声明为异步,因此至少有一个等待它。
当然,如果您在阅读数据时无所事事,可以执行以下操作:
public async Task UpdateData()
{
Textbox1.Text = "Retrieving member data ...";
MyReadData data = await Task.Run( () => ReadDatabaseData(...));
Textbox1.Text = "Finished reading data";
ProcessReadData(data);
}
将读取数据的异步过程不应执行任何与UI相关的操作:
public async Task<MyReadData> ReadDatabaseData(...)
{
using (var reader = mySql.ExecuteReader())
{
// Read the data, don't do anything UI-related
return myReadData;
}
}
我认为async-await的优点在于你的代码看起来像是同步执行的。没有竞争条件,您不需要使用互斥锁来保护代码中的关键部分。执行线程具有UI线程的上下文(类似于:它可以执行UI线程可以执行的操作),您不需要InvokeRequired
之类的东西。
答案 1 :(得分:0)
您应该在单独的线程上检索数据以使UI响应。尝试使用后台工作者。
这很简单。这是MSDN link。