我们都知道,某些场合下,在对数据库进行访问时,为了避免同步访问数据时所带来的延迟,我们需要改进设计,以提高程序执行效率。一方面,这可以给用户以良好的使用体验;另一方面,也降低了程序崩溃的可能性。为实现这一目的,我们采用异步方式来访问数据库。先看下面的代码
private
void
button2_Click(
object
sender, EventArgs e)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
using (SqlConnection cn = new SqlConnection(cnSettings.ConnectionString))//新建连接
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
cn.Open();
//新建命令
using (SqlCommand cmd = cn.CreateCommand())
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
cmd.CommandText = "WaitFor Delay '00:00:15' Select @@Version";
ver = (string)cmd.ExecuteScalar();
}
}
label1.Text = ver;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这个例子非常简单,这是同步的例子,:首先建立一个连接,打开连接后,创建一个SqlCommand,并设置命令,最后将执行后的值给到label控件.可以看到,整个例子都是在一个线程内进行的,并没有涉及到其他的线程,再看一下下面的例子.
private
void
button2_Click(
object
sender, EventArgs e)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
//新建联接
SqlConnection cn = new SqlConnection(cnSettings.ConnectionString);
cn.Open();
//新建命令
SqlCommand cmd = cn.CreateCommand();
cmd.CommandText = "WaitFor Delay '00:00:15' Select @@Version";
//在另外一个线程运行reader,ProcessResult可以看做是另外一个线程
cmd.BeginExecuteReader(new AsyncCallback(ProcessResult), cmd);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
可以看到,上面的代码就是异步读取数据库,实际上就是开启了另外的一个线程,下面为处理代码
public
void
ProcessResult(IAsyncResult ar)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
SqlCommand cmd = (SqlCommand)ar.AsyncState;
using (cmd.Connection)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
using (cmd)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
string ver = null;
SqlDataReader rdr = cmd.EndExecuteReader(ar);
if (rdr.Read())
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
ver = (string)rdr[0];
label1.BeginInvoke(new LabelHandler(UpdateLabel), ver);
}
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
下面是委托的代码
//
声明一个委托代理
public
delegate
void
LabelHandler(
string
text);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
public
void
UpdateLabel(
string
text)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
label1.Text = text;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
值得注意的是,在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。