20.1 什么是异步编程异步编程就是把耗时的操作放进一个单独的线程中进行处理。

20.2 同步方式存在的问题

 
  1. namespace 异步编程
  2. {
  3. public partial class Form1 : Form
  4. {
  5. public Form1()
  6. {
  7. InitializeComponent();
  8. txbUrl.Text = "http://dldir1.qq.com/qqfile/qq/QQ8.7/19113/QQ8.7.exe";
  9. }
  10. private void btnDownLoad_Click(object sender, EventArgs e)
  11. {
  12. rtbState.Text = "下载中.....";
  13. if (txbUrl.Text == string.Empty)
  14. {
  15. MessageBox.Show("请先输入下载地址!");
  16. return;
  17. }
  18. DownLoadFileSync(txbUrl.Text.Trim());
  19. }
  20. public void DownLoadFileSync(string url)
  21. {
  22. int BufferSize = 2084;
  23. byte[] BufferRead = new byte[BufferSize];
  24. string savePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\我的QQ.exe";
  25. FileStream fileStream = null;
  26. HttpWebResponse myWebResponse = null;
  27. if (File.Exists(savePath))
  28. {
  29. File.Delete(savePath);
  30. }
  31. fileStream = new FileStream(savePath, FileMode.OpenOrCreate);
  32. try
  33. {
  34. HttpWebRequest myHTTpWebRequest = (HttpWebRequest) WebRequest.Create(url);
  35. if (myHTTpWebRequest != null)
  36. {
  37. myWebResponse = (HttpWebResponse) myHTTpWebRequest.GetResponse();
  38. Stream responseStream = myWebResponse.GetResponseStream();
  39. int readSize = responseStream.Read(BufferRead, 0, BufferSize);
  40. while (readSize > 0)
  41. {
  42. fileStream.Write(BufferRead, 0, readSize);
  43. readSize = responseStream.Read(BufferRead, 0, BufferSize);
  44. }
  45. rtbState.Text = "文件下载完成,文件大小为:" + fileStream.Length + "下载路径为:" + savePath;
  46. }
  47. }
  48. catch (Exception ex)
  49. {
  50. rtbState.Text = "下载异常,异常信息为:" + ex.Message;
  51. }
  52. finally
  53. {
  54. if (myWebResponse != null)
  55. {
  56. myWebResponse.Close();
  57. }
  58. if (fileStream !=null)
  59. {
  60. fileStream.Close();
  61. }
  62. }
  63. }
  64. }
  65. }
虽然完成了文件下载功能,但却带来不好的用户体验,
点“下载”按钮后,程序处于“假死”状态。而且,“下载中....”也没有显示在界面中。
主线程忙于执行下载操作,无法空出时间来绘制窗体,导致在下载过程中出现“假死”现象。
异步编程可以很好的解决这个问题。

20.3 异步编程模型——APM(.NET不推荐)

         APM 是 Asynchronous Programming Mode (异步编程模式)的缩写。(在.NET Framework 4 和更早的版本中
        它允许程序用更少的线程去执行更多的操作。
        要分辨某个类是否实现异步编程模式,主要看该类是否实现了返回类型为 IAsyncResult 接口的 Beginxxx 和 Endxxx 方法。

1.Beginxxx 方法——开始执行异步操作

       在要获取文件中内容时,通常使用 FileStream 的同步方法 Read 进行读取,定义:
public abstract int Read(byte[] array, int offset, int count);
       该方法从文件流中读取字节块,然后将该数据写入给定的字节数组 array 中。这里 array 表示读取的字节块要被写入的地方, offset 表示从文件流读取字节的开始位置, count 表示最多读取的字节数。
       读取大文件时,调用 Read 方法会阻塞UI线程,导致文件没有读取前窗体无法响应。为了解决这个情况,.NET1.0就提出了异步编程模型,并为 FileStream 类提供了异步模型方法,即 BeginRead 方法。该方法会异步执行读取,并返回 IAsyncResult 接口的对象(该对象存储异步操作信息)。
public virtual IAsyncResult BeginRead(
byte[] buffer,
int offset,
int count,
AsyncCallback callback,
object state
)

该方法前面3个参数和同步方法 Read 一致,后两个参数 callback 和 state 则不同。

callbackType: System.AsyncCallback

异步操作完成后需要回调的方法,该方法必须匹配 AsyncCallback委托类型。

stateType: System.Object

传递给回调方法的对象,在回调方法中,可以查询 IAsyncResult 接口的 AsyncState 属性来读取该对象。

2.Endxxx 方法 ——结束异步操作

调用完 Beginxxx 方法后,还需要调用 Endxxx 方法来获取操作返回的结果。
Endxxx 返回的类型与同步方法相同,如 FileStream 的 EndRead 方法会返回一个 Int32 类型,代表从文件流中实际读取的字节数。
 
APM给出了4种方式来访问异步操作所得到的结果。
  1. 在调用 Beginxxx 方法的线程上调用 Endxxx 方法来得到异步操作的结果。然而这种方式会阻塞调用线程,使其一直挂起,直至操作完成。

  2. 在调用 Beginxxx 方法的线程查询 IAsyncResult 的 AsyncWaitHandle 属性,从而得到 WaitHandle 对象,接着调用该对象 WaitOne 方法来堵塞线程并等待操作完成,最后调用 Endxxx 方法获得操作结果。

  3. 在调用 Beginxxx 方法的线程上循环查询 IAsyncResult 的 IsComplete 属性,操作完成后调用 Endxxx 方法获得操作结果。

  4. 使用 AsyncCallback 委托来指定操作完成要调用的方法,在回调方法中调用 Endxxx 方法来获得异步操作返回的结果。

上面4种,前3种会堵塞调用线程。

第1种方式:

  1. public void DownLoadFileSync(string url)

  2. {

  3. int BufferSize = 2084;

  4. byte[] BufferRead = new byte[BufferSize];

  5. string savePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\我的QQ.exe";

  6. FileStream fileStream = null;

  7. HttpWebResponse myWebResponse = null;

  8. if (File.Exists(savePath))

  9. {

  10. File.Delete(savePath);

  11. }

  12. fileStream = new FileStream(savePath, FileMode.OpenOrCreate);

  13. try

  14. {

  15. HttpWebRequest myHTTpWebRequest = (HttpWebRequest) WebRequest.Create(url);

  16. if (myHTTpWebRequest != null)

  17. {

  18. //myWebResponse = (HttpWebResponse)myHTTpWebRequest.GetResponse();

  19. IAsyncResult result = myHTTpWebRequest.BeginGetResponse(null, null);//异步请求

  20. myWebResponse = (HttpWebResponse)myHTTpWebRequest.EndGetResponse(result);

  21. Stream responseStream = myWebResponse.GetResponseStream();

  22. int readSize = responseStream.Read(BufferRead, 0, BufferSize);

  23. while (readSize > 0)

  24. {

  25. fileStream.Write(BufferRead, 0, readSize);

  26. readSize = responseStream.Read(BufferRead, 0, BufferSize);

  27. }

  28. rtbState.Text = "文件下载完成,文件大小为:" + fileStream.Length + "下载路径为:" + savePath;

  29. }

  30. }

  31. catch (Exception ex)

  32. {

  33. rtbState.Text = "下载异常,异常信息为:" + ex.Message;

  34. }

  35. finally

  36. {

  37. if (myWebResponse != null)

  38. {

  39. myWebResponse.Close();

  40. }

  41. if (fileStream !=null)

  42. {

  43. fileStream.Close();

  44. }

  45. }

  46. }

DownLoadFileSync 方法通过调用 BeginGetResponse 方法来异步请求资源,执行该方法后立即返回UI线程。UI线程继续执行代码,遇到 EndGetResponse 方法,此方法堵塞UI线程,使得效果和同步方法实现一样。

  1. namespace 异步编程

  2. {

  3. public partial class Form1 : Form

  4. {

  5. public Form1()

  6. {

  7. InitializeComponent();

  8. txbUrl.Text = "http://dldir1.qq.com/qqfile/qq/QQ8.7/19113/QQ8.7.exe";

  9. }

  10. private delegate string AsyncMethodCaller(string fileurl);

  11. private SynchronizationContext sc;

  12. private void btnDownLoad_Click(object sender, EventArgs e)

  13. {

  14. rtbState.Text = "下载中.....";

  15. btnDownLoad.Enabled = false;

  16. if (txbUrl.Text == string.Empty)

  17. {

  18. MessageBox.Show("请先输入下载地址!");

  19. return;

  20. }

  21. //DownLoadFileSync(txbUrl.Text.Trim());

  22. sc = SynchronizationContext.Current;

  23. AsyncMethodCaller methodCaller = new AsyncMethodCaller(DownLoadFileSync);

  24. methodCaller.BeginInvoke(txbUrl.Text.Trim(), GetResult, null);

  25. }

  26. private void GetResult(IAsyncResult result)

  27. {

  28. AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;

  29. string returning = caller.EndInvoke(result);

  30. sc.Post(ShowState, returning);

  31. }

  32. private void ShowState(object result)

  33. {

  34. rtbState.Text = result.ToString();

  35. btnDownLoad.Enabled = true;

  36. }

  37. public string DownLoadFileSync(string url)

  38. {

  39. int BufferSize = 2084;

  40. byte[] BufferRead = new byte[BufferSize];

  41. string savePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\我的QQ.exe";

  42. FileStream fileStream = null;

  43. HttpWebResponse myWebResponse = null;

  44. if (File.Exists(savePath))

  45. {

  46. File.Delete(savePath);

  47. }

  48. fileStream = new FileStream(savePath, FileMode.OpenOrCreate);

  49. try

  50. {

  51. HttpWebRequest myHTTpWebRequest = (HttpWebRequest)WebRequest.Create(url);

  52. if (myHTTpWebRequest != null)

  53. {

  54. myWebResponse = (HttpWebResponse)myHTTpWebRequest.GetResponse();

  55. Stream responseStream = myWebResponse.GetResponseStream();

  56. int readSize = responseStream.Read(BufferRead, 0, BufferSize);

  57. while (readSize > 0)

  58. {

  59. fileStream.Write(BufferRead, 0, readSize);

  60. readSize = responseStream.Read(BufferRead, 0, BufferSize);

  61. }

  62. }

  63. return  string.Format("文件下载完成,文件大小为:" + fileStream.Length + "下载路径为:" + savePath);

  64. }

  65. catch (Exception ex)

  66. {

  67. return  string.Format("下载异常,异常信息为:" + ex.Message);

  68. }

  69. finally

  70. {

  71. if (myWebResponse != null)

  72. {

  73. myWebResponse.Close();

  74. }

  75. if (fileStream != null)

  76. {

  77. fileStream.Close();

  78. }

  79. }

  80. }

  81. }

  82. }

20.4 异步编程模型——EAP(.NET不推荐)

APM同样存在一些明显的问题,如不支持对异步操作的取消以及不能提供下载进度报告等。

微软.NET2.0发布提出了一个新的异步编程模式——基于事件的异步模式,EAP(Event-based Asynchronous Pattern)。

实现EPA的类具有一个或多个以 Async 为后缀的方法,以及对应的 Completed 事件,并且这些类支持异步方法的取消和进度报告。在.NET类库中只有部分类实现了EPA,共17个。

System.Object的派生类型:

  System.Activies.WorkflowInvoke  

  System.Deployment.Application.ApplicationDeployment

  System.Deployment.Application.InPlaceHosingManager

  System.Net.Mail.SmtpClient

  System.Net.PeerToPeer.PeerNameResolver

  System.Net.PeerToPeer.Collaboration.ContactManager

  System.Net.PeerToPeer.Collaboration.Peer

  System.Net.PeerToPeer.Collaboration.PeerContact

  System.Net.PeerToPeer.Collaboration.PeerNearMe

  System.ServiceModel.Activities.WorkflowControlClient

  System.ServiceModel.Discovery.AnnoucementClient

  System.ServiceModel.Discovery.DiscoveryClient

System.ComponentModel.Component的派生类型:

System.ComponentModel.BackgroundWorker

System.Media.SoundPlay

System.Net.WebClient

System.Net.NetworkInformation.Ping

System.Windows.Forms.PictureBox(继承于Control类,Control类派生于Component类)

使用最多的莫过于 BackgroundWorker 类了,该类经常被用于实现网络文件下载功能。

BackgroundWorker类

 

公共属性

 

属性名

说明

CancellationPending

获取一个值,指示应用程序是否已请求取消后台操作

IsBusy

获取一个值,指示 BackgroundWorker 是否正在运行异步操作。

WorkReportsProgress

获取或设置一个值,该值指示 BackgroundWorker 能否报告进度更新。

WorkerSupportsCancellation

获取或设置一个值,该值指示 BackgroundWorker 是否支持异步取消。

公共方法

 

名称

说明

CancelAsync

请求取消挂起的后台操作。

ReportProgress

引发 ProgressChanged 事件(官方这样解释我就要信?)

RunWorkerAsync

开始执行后台操作。

公共事件

 

名称

说明

DoWork

调用 RunWorkerAsync 时发生(官方是这么解释的,你想知道为什么调用RunWorkerAsync方法就会触发DoWork事件吗?

ProgressChanged

调用ReportProgress时发生(官方是这么解释的,你想知道为什么调用ReportProgress方法就会触发ProgressChanged事件吗?)

RunWorkerCompleted

当后台操作已完成、被取消或引发异常时发生。

 
  1. public partial class Form1 : Form

  2. {

  3. public int DownloadSize = 0;

  4. public string downloadPath = null;

  5. long totalSize = 0;

  6. const int BufferSize = 2048;

  7. byte[] BufferRead = new byte[BufferSize];

  8. FileStream filestream = null;

  9. HttpWebResponse myWebResponse = null;

  10. public Form1()

  11. {

  12. InitializeComponent();

  13. string url = "http://dldir1.qq.com/qqfile/qq/QQ8.7/19113/QQ8.7.exe";

  14. txbUrl.Text = url;

  15. this.btnPause.Enabled = false;

  16. GetTotalSize();

  17. downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.txbUrl.Text.Trim());

  18. if (File.Exists(downloadPath))

  19. {

  20. FileInfo fileInfo = new FileInfo(downloadPath);

  21. DownloadSize = (int)fileInfo.Length;

  22. progressBar1.Value = (int)((float)DownloadSize / (float)totalSize * 100);

  23. }

  24. // 使BackgroundWorker类支持ReportProgress和ReportProgress操作

  25. bgWorkerFileDownload.WorkerReportsProgress = true;

  26. bgWorkerFileDownload.WorkerSupportsCancellation = true;

  27. }

  28. private void btnDownLoad_Click(object sender, EventArgs e)

  29. {

  30. if (bgWorkerFileDownload.IsBusy != true)

  31. {

  32. bgWorkerFileDownload.RunWorkerAsync();

  33. filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);

  34. filestream.Seek(DownloadSize, SeekOrigin.Begin);

  35. this.btnDownLoad.Enabled = false;

  36. this.btnPause.Enabled = true;

  37. }

  38. else

  39. {

  40. MessageBox.Show("正在执行操作,请稍后");

  41. }

  42. }

  43. private void btnPause_Click(object sender, EventArgs e)

  44. {

  45. if (bgWorkerFileDownload.IsBusy && bgWorkerFileDownload.WorkerSupportsCancellation == true)

  46. {

  47. bgWorkerFileDownload.CancelAsync(); // 取消下载操作

  48. }

  49. }

  50. private void bgWorkerFileDownload_DoWork(object sender, DoWorkEventArgs e)

  51. {

  52. BackgroundWorker bgworker = sender as BackgroundWorker;

  53. try

  54. {

  55. // Do the DownLoad operation

  56. // Initialize an HttpWebRequest object

  57. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());

  58. // If the part of the file have been downloaded,

  59. // The server should start sending data from the DownloadSize to the end of the data in the HTTP entity.

  60. if (DownloadSize != 0)

  61. {

  62. myHttpWebRequest.AddRange(DownloadSize);

  63. }

  64. // assign HttpWebRequest instance to its request field.

  65. myWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

  66. Stream responseStream = myWebResponse.GetResponseStream();

  67. int readSize = 0;

  68. while (true)

  69. {

  70. if (bgworker.CancellationPending == true)

  71. {

  72. e.Cancel = true;

  73. break;

  74. }

  75. readSize = responseStream.Read(BufferRead, 0, BufferRead.Length);

  76. if (readSize > 0)

  77. {

  78. DownloadSize += readSize;

  79. int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);

  80. filestream.Write(BufferRead, 0, readSize);

  81. // 报告进度,引发ProgressChanged事件的发生

  82. bgworker.ReportProgress(percentComplete);

  83. }

  84. else

  85. {

  86. break;

  87. }

  88. }

  89. }

  90. catch

  91. {

  92. throw;

  93. }

  94. }

  95. private void bgWorkerFileDownload_ProgressChanged(object sender, ProgressChangedEventArgs e)

  96. {

  97. this.progressBar1.Value = e.ProgressPercentage;

  98. }

  99. private void bgWorkerFileDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

  100. {

  101. if (e.Error != null)

  102. {

  103. MessageBox.Show(e.Error.Message);

  104. myWebResponse.Close();

  105. }

  106. else if (e.Cancelled)

  107. {

  108. MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));

  109. myWebResponse.Close();

  110. filestream.Close();

  111. filestream.Close();

  112. this.btnDownLoad.Enabled = true;

  113. this.btnPause.Enabled = false;

  114. }

  115. else

  116. {

  117. MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", downloadPath, totalSize));

  118. this.btnDownLoad.Enabled = false;

  119. this.btnPause.Enabled = false;

  120. myWebResponse.Close();

  121. filestream.Close();

  122. }

  123. }

  124. private void GetTotalSize() // 获得文件总大小

  125. {

  126. HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());

  127. HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse();

  128. totalSize = response.ContentLength;

  129. response.Close();

  130. }

  131. }

[你必须知道的异步编程]——基于事件的异步编程模式

http://www.cnblogs.com/zhili/archive/2013/05/11/EAP.html

20.5 异步编程模型——TAP

前2种模式在.NET中被标识为不推荐使用的实现方式。

在.NET 4.0中,微软提供更简单的异步方式——基于任务的异步模式,即TAP。

该模式主要使用 System.Threading.Tasks.Task和Task<T> 类来完成异步编程,相对于前面两种异步模式来讲,TAP使异步编程模式更加简单(因为这里我们只需要关注 Task 这个类的使用),同时TAP也是微软推荐使用的异步编程模式。

        基于任务的异步模式,只使用一个方法就能表示异步操作的开始和完成。只需要一个以 TaskAsync 为后缀的方法,通过向该方法传入 CancellationToken 参数,就可以完成异步编程了。而且还可以通过 IProgress<T> 接口来实现进度报告的功能。
 
 

20.6 C#5.0 中的 async 和 await

 
太大段代码,还是看看《C#本质论》18章 和《精通C#(第6版)》19章
 
 
 

《learning hard C#学习笔记》读书笔记(20)异步编程的更多相关文章

  1. 《Hands-On Machine Learning with Scikit-Learn&TensorFlow》读书笔记

    一 机器学习概览 机器学习的广义概念是:机器学习是让计算机具有学习的能力,无需进行明确编程. 机器学习的工程性概念是:计算机程序利用经验E学习任务T,性能是P,如果针对任务T的性能P随着经验E不断增长 ...

  2. 鸟哥Linux私房菜 基础学习篇读书笔记(10):Linux磁盘和文件系统管理(3)

    本文总结了Linux操作系统来管理我们的硬盘和文件系统需要使用命令.当我们在系统中增加一个硬盘驱动器.什么是我们需要去通过这个硬盘就可以真正使用步骤?下面步骤: (1)对磁盘进行分区,新建能够使用的分 ...

  3. 鸟哥的Linux私房菜 基础学习篇读书笔记(9):Linux磁盘与文件系统管理(2)

    上一篇文章主要从理论上分析了Linux的Ext2文件系统.这一篇主要解说怎样查看Linux的文件系统的容量以及解说Linux文件系统中的连接文件. 能够通过df和du命令来查看磁盘与文件夹的容量.df ...

  4. 人体和电脑的关系——鸟哥的LINUX私房菜基础学习篇读书笔记

    CUP=脑袋: 每个人会做的事情都不一样(指令集的差异),但主要都是通过脑袋来判断与控制身体各部分的行动 内存=脑袋中存放正在思考的数据区块: 在实际活动过程中,我们的脑袋需要有外界刺激的数据(例如光 ...

  5. Deep Learning 2_深度学习UFLDL教程:矢量化编程(斯坦福大学深度学习教程)

    1前言 本节主要是让人用矢量化编程代替效率比较低的for循环. 在前一节的Sparse Autoencoder练习中已经实现了矢量化编程,所以与前一节的区别只在于本节训练集是用MINIST数据集,而上 ...

  6. 人生效率手册:如何卓有成效地过好每一天--By张萌姐姐--读书笔记

    读书笔记:<人生效率手册>:如何卓有成效地过好每一天--By张萌姐姐... 整本书看完的感受: 这本书主要讲的是生活中我们需要给自己一个目标,然后通过自己的努力去实现这个目标,书中说的很多 ...

  7. 强化学习读书笔记 - 06~07 - 时序差分学习(Temporal-Difference Learning)

    强化学习读书笔记 - 06~07 - 时序差分学习(Temporal-Difference Learning) 学习笔记: Reinforcement Learning: An Introductio ...

  8. 【Deep Learning读书笔记】深度学习中的概率论

    本文首发自公众号:RAIS,期待你的关注. 前言 本系列文章为 <Deep Learning> 读书笔记,可以参看原书一起阅读,效果更佳. 概率论 机器学习中,往往需要大量处理不确定量,或 ...

  9. 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要系列文章索引

    从发表第一篇文章到最后一篇文章,时间间隔有整整一个月,虽只有5篇文章,但每一篇文章都是我吸收<LEARNING HARD C#学习笔记>这本书的内容要点及网上各位大牛们的经验,没有半点废话 ...

随机推荐

  1. c# .Net并行和多线程编程之Task学习记录!

    任务Task和线程Thread的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线 ...

  2. web页面之响应式布局

    一.什么是响应式布局? 响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本.这个概念是为解决移动互联网 ...

  3. textView的提醒文字

    如果我们想提交一些备注信息,会想到用textFiled和textView两种控件去实现 1.提醒文字是textFiled的特有属性,但是textFiled显示文本只有一行,不能实现我们输入较多文字的情 ...

  4. thinkphp模型

    1.获取系统常量信息的方法:在控制器DengLuController里面下写入下面的方法,然后调用该方法. public function test() { //echo "这是测试的&qu ...

  5. linux系统运维常用基本命令详解

      1.ls 文件属性:  -:普通文件  d:目录文件  b:块设备  c:字符设备文件  l:符号连接文件  p:命令管道  s:套接字文件  文件权限: 9位数字,每3位一组  文件硬链接次数  ...

  6. Android之仿微信图片选择器

    先上效果图.第一张图显示的是“相机”文件夹中的所有图片:通过点击多张图片可以到第二张图所示的效果(被选择的图片会变暗,同时选择按钮变亮):点击最下面的那一栏可以到第三张图所示的效果(显示手机中所有包含 ...

  7. SQL Server数据库定时自动备份

    SQL Server 数据库定时自动备份[转]   在SQL Server中出于数据安全的考虑,所以需要定期的备份数据库.而备份数据库一般又是在凌晨时间基本没有数据库操作的时候进行,所以我们不可能要求 ...

  8. apache和tomcat有什么不同,为什么要整合apache 和tomcat

    1. Apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展.2. Apache和Tomcat都可以做为独立的web服务器来运行,但是Ap ...

  9. 第4月第1天 makefile automake

    1. gnu make的函数调用是$,比如 $(subst ee,EE,feet on the street) 规则中“TARGETS”可以是空格分开的多个文件名 a all: echo $(subs ...

  10. C和指针 第十五章 二进制I/O

    二进制I/O 数据写入到文件效率最高的是用二进制形式写入,二进制输出避免了在数值转换为字符串过程中,所涉及的开销和精度损失,但而精致并非人眼所能阅读,所以这个技巧只有当数据被另一个程序按顺序读取才能使 ...