×

异步io 异步

什么是同步IO和异步IO?如何使用backgroundWorker进行异步操作

admin admin 发表于2022-05-31 01:24:53 浏览120 评论0

抢沙发发表评论

什么是同步IO和异步IO


异步文件IO也就是重叠IO。 在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。 如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO方式。 同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。 异步IO在请求完成时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完成,也可以通过一个事件对象来通知应用程序。 例如DeviceIoControl这个函数,他就可以通过参数指定是同步或异步,如果是同步的话,则该函数将会等待结果返回后,才执行下一条语句。如果是异步的话,DeviceIoControl调用后马上返回,如果参数正确,则回返回ERROR_IO_PENDING(忘了怎样写,不过肯定是有PENDING这个词),然后你可以通过GetOverlappedResult获取返回结果,是一个overlap结构,是在你调用DeviceIoControl的最后一个参数传进去的`` 简单的说``同步在编程里,一般是指某个操作执行完后,才可以执行后面的操作``拿到IO上来说``就是我要做完这个IO操作``才继续后面的操作``` 异步则是,我交带了某个操作给系统(可以是windows,也可以是你自己的库),我呆会过来拿,我现在要去忙别的``拿到IO上说``我交带了某个IO操作给系统。。。。。

如何使用backgroundWorker进行异步操作


组件介绍

BackgroundWorker类位于System.ComponentModel 命名空间中,通过该类在单独的线程上执行操作实现基于事件的异步模式。下面对BackgroundWorker类的主要成员进行介绍。

BackgroundWorker类的第1个主要方法是RunWorkerAsync,该方法提交一个以异步方式启动运行操作的请求,发出请求后,将引发 DoWork 事件,在事件处理程序中开始执行异步操作代码。RunWorkerAsync 方法签名如下,

publicvoidRunWorkerAsync();

publicvoidRunWorkerAsync(Object argument);

如果异步操作需要操作参数,可以将其作为argument参数提供,由于参数类型为Object,因此访问时可能需要进行类型转换。

CancelAsync 方
法提交终止异步操作的请求,并将 CancellationPending 属性设置为 true。需要注意的是,CancelAsync
方法是否调用成功,同WorkerSupportsCancellation
属性相关,如果允许取消执行的异步操作,需将WorkerSupportsCancellation
属性设置为true,否则调用该方法将抛出异常。CancelAsync方法不含参数,方法签名如下,

publicvoid CancelAsync();

调用 CancelAsync
方法时,BackgroundWorker的 CancellationPending
属性值将被设置为true,因此在编写单独线程中执行的辅助方法时,代码中应定期检查 CancellationPending
属性,查看是否已将该属性设置为 true,如果为true,应该结束辅助方法的执行。有一点需要注意的是,DoWork
事件处理程序中的代码有可能在发出取消请求时已经完成处理工作,因此,DoWork事件处理程序或辅助方法可能会错过设置
CancellationPending属性为true的时机。在这种情况下,即使调用
CancelAsync方法发出了取消异步操作请求,RunWorkerCompleted
事件处理程序中RunWorkerCompletedEventArgs 参数的 Cancelled 标志也不会被设置为
true,这是在多线程编程中经常会出现的竞争条件问题,因此编写代码的时候需要考虑。

在执行异步操作时,如果需要跟踪异步操作执行进度,BackgroundWorker类提供了 ReportProgress 方法,调用该方法将引发 ProgressChanged 事件,通过注册该事件在事件处理程序中获取异步执行进度信息。方法签名如下:

publicvoidReportProgress(int percentProgress);

publicvoidReportProgress(int percentProgress,Object userState);

该方法包含两个版本,percentProgress表示进度百分比,取值为0-100,userState为可选参数表示自定义用户状态。

同CancelAsync 方法一样,BackgroundWorker的WorkerReportsProgress 属性设置为 true时,ReportProgress 方法才会调用成功,否则将引发InvalidOperationException异常。

上面已经提到了
BackgroundWorker的3个属性,CancellationPending用来提示操作是否已经取
消,WorkerReportsProgress和WorkerSupportsCancellation分别用来设置是否允许进度汇报和进行取消操作。

publicboolCancellationPending { get; }

publicboolWorkerReportsProgress { get; set; }

publicboolWorkerSupportsCancellation { get; set; }

另外一个会用到的属性是IsBusy,

publicbool IsBusy { get; }

通过该属性查询BackgroundWorker实例是否正在运行异步操作,如果 BackgroundWorker 正在运行异步操作,则为true,否则为false。

BackgroundWorker类包含3个事件,在事件处理程序中可进行异步操作辅助代码编写和同用户界面信息交互。

publiceventDoWorkEventHandler DoWork;

publiceventProgressChangedEventHandler ProgressChanged;

publiceventRunWorkerCompletedEventHandler RunWorkerCompleted;

DoWork事
件处理程序用来调用辅助方法进行实际处理操作,由于该事件处理程序在不同于UI的线程上执行,因此需要确保在 DoWork
事件处理程序中不操作任何用户界面对象。如果辅助方法需要参数支持,可以通过RunWorkerAsync方法传入,在 DoWork
事件处理程序中,通过 DoWorkEventArgs.Argument 属性提取该参数。在异步操作期间,可以通过
ProgressChanged事件处理程序获取异步操作进度信息,通过RunWorkerCompleted
事件处理程序获取异步操作结果信息,在ProgressChanged和RunWorkerCompleted的事件处理程序中可以安全的同用户界面进行
通信。

应用示例

下面通过一个简单的示例来演示BackgroundWorker组件的典型应用。在本示例中,实现一个数值的求和操作,该操作本身运行很快,为模拟处理过程有一个可感知的时间段,在辅助方法中调用了Thread.Sleep方法。

示例程序通过Windows Forms展示,显示了对1-100的数值进行求和操作,界面如下,

图1:应用程序界面

下面对主要实现代码进行说明,先看一下BackgroundWorker类的初始化,在初始化过程中注册了3个事件,允许异步辅助方法调用,以及异步操作进度通知和操作取消。

private System.ComponentModel.BackgroundWorker backgroundWorker1;

private void InitializeBackgoundWorker()

{

this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();

this.backgroundWorker1.WorkerReportsProgress = true;

this.backgroundWorker1.WorkerSupportsCancellation = true;

this.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);

this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);

}

通过StartAsync按钮事件处理程序开始异步处理操作请求,事件处理程序如下,

private void startAsyncButton_Click(object sender, EventArgs e)

{

resultLabel.Text = String.Empty;

this.numericUpDown1.Enabled = false;

this.startAsyncButton.Enabled = false;

this.cancelAsyncButton.Enabled = true;

//获取计算数值.

int numberToCompute = (int)numericUpDown1.Value;

//启动异步操作.

backgroundWorker1.RunWorkerAsync(numberToCompute);

}

startAsyncButton_Click处理程序首先对一些界面控件进行状态设置,然后调用BackgroundWorker实例的RunWorkerAsync方法开始执行异步操作,而此时就会触发DoWork事件。

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

BackgroundWorker worker = sender as BackgroundWorker;

e.Result = ComputeAdd((int)e.Argument, worker, e);

}

在DoWork事件处理程序中,通过DoWorkEventArgs.Argument属
性获取传入的参数传递给ComputeAdd辅助方法,并把处理结果保存到DoWorkEventArgs.Result属性中,最后在
RunWorkerCompleted 事件处理程序的RunWorkerCompletedEventArgs.Result
属性中获取处理结果。如果在DoWork事件处理程序中出现异常,则 BackgroundWorker 将捕获该异常并将其传递到
RunWorkerCompleted 事件处理程序,在该事件处理程序中,异常信息作为 RunWorkerCompletedEventArgs 的
Error 属性公开。

private long ComputeAdd(int n, BackgroundWorker worker, DoWorkEventArgs e)

{

long result = 0;

for (int i = 1; i 《= n; i++)

{

if (worker.CancellationPending)

{

e.Cancel = true;

break;

}

else

{

result += i;

Thread.Sleep(500);

int percentComplete = (int)((float)i / (float)n * 100);

worker.ReportProgress(percentComplete);

}

}

return result;

}

在辅助方法中,代码定期访问BackgroundWorker实
例的CancellationPending属性,如果调用了BackgroundWorker的CancelAsync
方法,那么CancellationPending属性值就会被设置为true,辅助方法就结束执行。另外,在辅助方法中实现了进度汇报功能,通过调用
worker.ReportProgress方法触发ProgressChanged事件,接着通过ProgressChanged事件处理程序来更新进
度显示。

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

{

this.progressBar1.Value = e.ProgressPercentage;

}

最后,在
RunWorkerCompleted事件处理程序中可以得到异步处理结果信息,分析异步操作是正常执行结束还是在处理中被取消或者是执行出现错误异常而
终止。对于处理结果信息的访问有一个标准的顺序,先是判断异步处理是否异常结束,接着判断是否执行了取消操作,最后访问处理结果。

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

if (e.Error != null)

{

MessageBox.Show(e.Error.Message);

}

else if (e.Cancelled)

{

resultLabel.Text = “Canceled“;

}

else

{

resultLabel.Text = e.Result.ToString();

}

this.numericUpDown1.Enabled = true;

startAsyncButton.Enabled = true;

cancelAsyncButton.Enabled = false;

}

编程中异步io和同步io有什么区别


异步文件IO也就是重叠IO。
在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。
如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO方式。
同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。
-异步io